blob: 0865ecd0b417c2ae699778162677f8672183a1c4 [file] [log] [blame]
Bob Badoura99ac622021-10-25 16:21:00 -07001// Copyright 2021 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package compliance
16
17import (
18 "fmt"
19 "sort"
20 "strings"
21)
22
23// Resolution describes an action to resolve one or more license conditions.
24//
25// `AttachesTo` identifies the target node that when distributed triggers the action.
26// `ActsOn` identifies the target node that is the object of the action.
27// `Resolves` identifies one or more license conditions that the action resolves.
28//
29// e.g. Suppose an MIT library is linked to a binary that also links to GPL code.
30//
31// A resolution would attach to the binary to share (act on) the MIT library to
32// resolve the restricted condition originating from the GPL code.
33type Resolution struct {
34 attachesTo, actsOn *TargetNode
35 cs *LicenseConditionSet
36}
37
38// AttachesTo returns the target node the resolution attaches to.
39func (r Resolution) AttachesTo() *TargetNode {
40 return r.attachesTo
41}
42
43// ActsOn returns the target node that must be acted on to resolve the condition.
44//
45// i.e. The node for which notice must be given or whose source must be shared etc.
46func (r Resolution) ActsOn() *TargetNode {
47 return r.actsOn
48}
49
50// Resolves returns the set of license condition the resolution satisfies.
51func (r Resolution) Resolves() *LicenseConditionSet {
52 return r.cs.Copy()
53}
54
55// asString returns a string representation of the resolution.
56func (r Resolution) asString() string {
57 var sb strings.Builder
58 cl := r.cs.AsList()
59 sort.Sort(cl)
60 fmt.Fprintf(&sb, "%s -> %s -> %s", r.attachesTo.name, r.actsOn.name, cl.String())
61 return sb.String()
62}
63
64// ResolutionList represents a partial order of Resolutions ordered by
65// AttachesTo() and ActsOn() leaving `Resolves()` unordered.
66type ResolutionList []Resolution
67
68// Len returns the count of elements in the list.
69func (l ResolutionList) Len() int { return len(l) }
70
71// Swap rearranges 2 elements so that each occupies the other's former position.
72func (l ResolutionList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
73
74// Less returns true when the `i`th element is lexicographically less than tht `j`th.
75func (l ResolutionList) Less(i, j int) bool {
76 if l[i].attachesTo.name == l[j].attachesTo.name {
77 return l[i].actsOn.name < l[j].actsOn.name
78 }
79 return l[i].attachesTo.name < l[j].attachesTo.name
80}
81
82// String returns a string representation of the list.
83func (rl ResolutionList) String() string {
84 var sb strings.Builder
85 fmt.Fprintf(&sb, "[")
86 sep := ""
87 for _, r := range rl {
88 fmt.Fprintf(&sb, "%s%s", sep, r.asString())
89 sep = ", "
90 }
91 fmt.Fprintf(&sb, "]")
92 return sb.String()
93}
94
95// AllConditions returns the union of all license conditions resolved by any
96// element of the list.
97func (rl ResolutionList) AllConditions() *LicenseConditionSet {
98 result := newLicenseConditionSet()
99 for _, r := range rl {
100 result.AddSet(r.cs)
101 }
102 return result
103}
104
105// ByName returns the sub-list of resolutions resolving conditions matching
106// `names`.
107func (rl ResolutionList) ByName(names ConditionNames) ResolutionList {
108 result := make(ResolutionList, 0, rl.CountByName(names))
109 for _, r := range rl {
110 if r.Resolves().HasAnyByName(names) {
111 result = append(result, Resolution{r.attachesTo, r.actsOn, r.cs.ByName(names)})
112 }
113 }
114 return result
115}
116
117// CountByName returns the number of resolutions resolving conditions matching
118// `names`.
119func (rl ResolutionList) CountByName(names ConditionNames) int {
120 c := 0
121 for _, r := range rl {
122 if r.Resolves().HasAnyByName(names) {
123 c++
124 }
125 }
126 return c
127}
128
129// CountConditionsByName returns a count of distinct resolution/conditions
130// pairs matching `names`.
131//
132// A single resolution might resolve multiple conditions matching `names`.
133func (rl ResolutionList) CountConditionsByName(names ConditionNames) int {
134 c := 0
135 for _, r := range rl {
136 c += r.Resolves().CountByName(names)
137 }
138 return c
139}
140
141// ByAttachesTo returns the sub-list of resolutions attached to `attachesTo`.
142func (rl ResolutionList) ByAttachesTo(attachesTo *TargetNode) ResolutionList {
143 result := make(ResolutionList, 0, rl.CountByActsOn(attachesTo))
144 for _, r := range rl {
145 if r.attachesTo == attachesTo {
146 result = append(result, r)
147 }
148 }
149 return result
150}
151
152// CountByAttachesTo returns the number of resolutions attached to
153// `attachesTo`.
154func (rl ResolutionList) CountByAttachesTo(attachesTo *TargetNode) int {
155 c := 0
156 for _, r := range rl {
157 if r.attachesTo == attachesTo {
158 c++
159 }
160 }
161 return c
162}
163
164// ByActsOn returns the sub-list of resolutions matching `actsOn`.
165func (rl ResolutionList) ByActsOn(actsOn *TargetNode) ResolutionList {
166 result := make(ResolutionList, 0, rl.CountByActsOn(actsOn))
167 for _, r := range rl {
168 if r.actsOn == actsOn {
169 result = append(result, r)
170 }
171 }
172 return result
173}
174
175// CountByActsOn returns the number of resolutions matching `actsOn`.
176func (rl ResolutionList) CountByActsOn(actsOn *TargetNode) int {
177 c := 0
178 for _, r := range rl {
179 if r.actsOn == actsOn {
180 c++
181 }
182 }
183 return c
184}
185
186// ByOrigin returns the sub-list of resolutions resolving license conditions
187// originating at `origin`.
188func (rl ResolutionList) ByOrigin(origin *TargetNode) ResolutionList {
189 result := make(ResolutionList, 0, rl.CountByOrigin(origin))
190 for _, r := range rl {
191 if r.Resolves().HasAnyByOrigin(origin) {
192 result = append(result, Resolution{r.attachesTo, r.actsOn, r.cs.ByOrigin(origin)})
193 }
194 }
195 return result
196}
197
198// CountByOrigin returns the number of resolutions resolving license conditions
199// originating at `origin`.
200func (rl ResolutionList) CountByOrigin(origin *TargetNode) int {
201 c := 0
202 for _, r := range rl {
203 if r.Resolves().HasAnyByOrigin(origin) {
204 c++
205 }
206 }
207 return c
208}