blob: d972eb9c48ae76d73636ea22486aaca5f5f5d6d8 [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)
20
21// NewLicenseConditionSet creates a new instance or variable of *LicenseConditionSet.
22func NewLicenseConditionSet(conditions ...LicenseCondition) *LicenseConditionSet {
23 cs := newLicenseConditionSet()
24 cs.Add(conditions...)
25 return cs
26}
27
28// LicenseConditionSet describes a mutable set of immutable license conditions.
29type LicenseConditionSet struct {
30 // conditions describes the set of license conditions i.e. (condition name, origin target) pairs
31 // by mapping condition name -> origin target -> true.
32 conditions map[string]map[*TargetNode]bool
33}
34
35// Add makes all `conditions` members of the set if they were not previously.
36func (cs *LicenseConditionSet) Add(conditions ...LicenseCondition) {
37 if len(conditions) == 0 {
38 return
39 }
40 for _, lc := range conditions {
41 if _, ok := cs.conditions[lc.name]; !ok {
42 cs.conditions[lc.name] = make(map[*TargetNode]bool)
43 }
44 cs.conditions[lc.name][lc.origin] = true
45 }
46}
47
48// AddSet makes all elements of `conditions` members of the set if they were not previously.
49func (cs *LicenseConditionSet) AddSet(other *LicenseConditionSet) {
50 if len(other.conditions) == 0 {
51 return
52 }
53 for name, origins := range other.conditions {
54 if len(origins) == 0 {
55 continue
56 }
57 if _, ok := cs.conditions[name]; !ok {
58 cs.conditions[name] = make(map[*TargetNode]bool)
59 }
60 for origin := range origins {
61 cs.conditions[name][origin] = other.conditions[name][origin]
62 }
63 }
64}
65
66// ByName returns a list of the conditions in the set matching `names`.
67func (cs *LicenseConditionSet) ByName(names ...ConditionNames) *LicenseConditionSet {
68 other := newLicenseConditionSet()
69 for _, cn := range names {
70 for _, name := range cn {
71 if origins, ok := cs.conditions[name]; ok {
72 other.conditions[name] = make(map[*TargetNode]bool)
73 for origin := range origins {
74 other.conditions[name][origin] = true
75 }
76 }
77 }
78 }
79 return other
80}
81
82// HasAnyByName returns true if the set contains any conditions matching `names` originating at any target.
83func (cs *LicenseConditionSet) HasAnyByName(names ...ConditionNames) bool {
84 for _, cn := range names {
85 for _, name := range cn {
86 if origins, ok := cs.conditions[name]; ok {
87 if len(origins) > 0 {
88 return true
89 }
90 }
91 }
92 }
93 return false
94}
95
96// CountByName returns the number of conditions matching `names` originating at any target.
97func (cs *LicenseConditionSet) CountByName(names ...ConditionNames) int {
98 size := 0
99 for _, cn := range names {
100 for _, name := range cn {
101 if origins, ok := cs.conditions[name]; ok {
102 size += len(origins)
103 }
104 }
105 }
106 return size
107}
108
109// ByOrigin returns all of the conditions that originate at `origin` regardless of name.
110func (cs *LicenseConditionSet) ByOrigin(origin *TargetNode) *LicenseConditionSet {
111 other := newLicenseConditionSet()
112 for name, origins := range cs.conditions {
113 if _, ok := origins[origin]; ok {
114 other.conditions[name] = make(map[*TargetNode]bool)
115 other.conditions[name][origin] = true
116 }
117 }
118 return other
119}
120
121// HasAnyByOrigin returns true if the set contains any conditions originating at `origin` regardless of condition name.
122func (cs *LicenseConditionSet) HasAnyByOrigin(origin *TargetNode) bool {
123 for _, origins := range cs.conditions {
124 if _, ok := origins[origin]; ok {
125 return true
126 }
127 }
128 return false
129}
130
131// CountByOrigin returns the number of conditions originating at `origin` regardless of condition name.
132func (cs *LicenseConditionSet) CountByOrigin(origin *TargetNode) int {
133 size := 0
134 for _, origins := range cs.conditions {
135 if _, ok := origins[origin]; ok {
136 size++
137 }
138 }
139 return size
140}
141
142// AsList returns a list of all the conditions in the set.
143func (cs *LicenseConditionSet) AsList() ConditionList {
144 result := make(ConditionList, 0, cs.Count())
145 for name, origins := range cs.conditions {
146 for origin := range origins {
147 result = append(result, LicenseCondition{name, origin})
148 }
149 }
150 return result
151}
152
153// Count returns the number of conditions in the set.
154func (cs *LicenseConditionSet) Count() int {
155 size := 0
156 for _, origins := range cs.conditions {
157 size += len(origins)
158 }
159 return size
160}
161
162// Copy creates a new LicenseCondition variable with the same value.
163func (cs *LicenseConditionSet) Copy() *LicenseConditionSet {
164 other := newLicenseConditionSet()
165 for name := range cs.conditions {
166 other.conditions[name] = make(map[*TargetNode]bool)
167 for origin := range cs.conditions[name] {
168 other.conditions[name][origin] = cs.conditions[name][origin]
169 }
170 }
171 return other
172}
173
174// HasCondition returns true if the set contains any condition matching both `names` and `origin`.
175func (cs *LicenseConditionSet) HasCondition(names ConditionNames, origin *TargetNode) bool {
176 for _, name := range names {
177 if origins, ok := cs.conditions[name]; ok {
178 _, isPresent := origins[origin]
179 if isPresent {
180 return true
181 }
182 }
183 }
184 return false
185}
186
187// IsEmpty returns true when the set of conditions contains zero elements.
188func (cs *LicenseConditionSet) IsEmpty() bool {
189 for _, origins := range cs.conditions {
190 if 0 < len(origins) {
191 return false
192 }
193 }
194 return true
195}
196
197// RemoveAllByName changes the set to delete all conditions matching `names`.
198func (cs *LicenseConditionSet) RemoveAllByName(names ...ConditionNames) {
199 for _, cn := range names {
200 for _, name := range cn {
201 delete(cs.conditions, name)
202 }
203 }
204}
205
206// Remove changes the set to delete `conditions`.
207func (cs *LicenseConditionSet) Remove(conditions ...LicenseCondition) {
208 for _, lc := range conditions {
209 if _, isPresent := cs.conditions[lc.name]; !isPresent {
210 panic(fmt.Errorf("attempt to remove non-existent condition: %q", lc.asString(":")))
211 }
212 if _, isPresent := cs.conditions[lc.name][lc.origin]; !isPresent {
213 panic(fmt.Errorf("attempt to remove non-existent origin: %q", lc.asString(":")))
214 }
215 delete(cs.conditions[lc.name], lc.origin)
216 }
217}
218
219// removeSet changes the set to delete all conditions also present in `other`.
220func (cs *LicenseConditionSet) RemoveSet(other *LicenseConditionSet) {
221 for name, origins := range other.conditions {
222 if _, isPresent := cs.conditions[name]; !isPresent {
223 continue
224 }
225 for origin := range origins {
226 delete(cs.conditions[name], origin)
227 }
228 }
229}
230
231// compliance-only LicenseConditionSet methods
232
233// newLicenseConditionSet constructs a set of `conditions`.
234func newLicenseConditionSet() *LicenseConditionSet {
235 return &LicenseConditionSet{make(map[string]map[*TargetNode]bool)}
236}
237
238// add changes the set to include each element of `conditions` originating at `origin`.
239func (cs *LicenseConditionSet) add(origin *TargetNode, conditions ...string) {
240 for _, name := range conditions {
241 if _, ok := cs.conditions[name]; !ok {
242 cs.conditions[name] = make(map[*TargetNode]bool)
243 }
244 cs.conditions[name][origin] = true
245 }
246}
247
248// asStringList returns the conditions in the set as `separator`-separated (origin, condition-name) pair strings.
249func (cs *LicenseConditionSet) asStringList(separator string) []string {
250 result := make([]string, 0, cs.Count())
251 for name, origins := range cs.conditions {
252 for origin := range origins {
253 result = append(result, origin.name+separator+name)
254 }
255 }
256 return result
257}
258
259// conditionNamesArray implements a `contains` predicate for arrays of ConditionNames
260type conditionNamesArray []ConditionNames
261
262func (cn conditionNamesArray) contains(name string) bool {
263 for _, names := range cn {
264 if names.Contains(name) {
265 return true
266 }
267 }
268 return false
269}