blob: 1ad15cab4cc857a67f703e60a6fc8dec89877a8c [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
Bob Badourfa739da2021-10-25 16:21:00 -0700153// Names returns a list of the names of the conditions in the set.
154func (cs *LicenseConditionSet) Names() []string {
155 result := make([]string, 0, len(cs.conditions))
156 for name := range cs.conditions {
157 result = append(result, name)
158 }
159 return result
160}
161
Bob Badoura99ac622021-10-25 16:21:00 -0700162// Count returns the number of conditions in the set.
163func (cs *LicenseConditionSet) Count() int {
164 size := 0
165 for _, origins := range cs.conditions {
166 size += len(origins)
167 }
168 return size
169}
170
171// Copy creates a new LicenseCondition variable with the same value.
172func (cs *LicenseConditionSet) Copy() *LicenseConditionSet {
173 other := newLicenseConditionSet()
174 for name := range cs.conditions {
175 other.conditions[name] = make(map[*TargetNode]bool)
176 for origin := range cs.conditions[name] {
177 other.conditions[name][origin] = cs.conditions[name][origin]
178 }
179 }
180 return other
181}
182
183// HasCondition returns true if the set contains any condition matching both `names` and `origin`.
184func (cs *LicenseConditionSet) HasCondition(names ConditionNames, origin *TargetNode) bool {
185 for _, name := range names {
186 if origins, ok := cs.conditions[name]; ok {
187 _, isPresent := origins[origin]
188 if isPresent {
189 return true
190 }
191 }
192 }
193 return false
194}
195
196// IsEmpty returns true when the set of conditions contains zero elements.
197func (cs *LicenseConditionSet) IsEmpty() bool {
198 for _, origins := range cs.conditions {
199 if 0 < len(origins) {
200 return false
201 }
202 }
203 return true
204}
205
206// RemoveAllByName changes the set to delete all conditions matching `names`.
207func (cs *LicenseConditionSet) RemoveAllByName(names ...ConditionNames) {
208 for _, cn := range names {
209 for _, name := range cn {
210 delete(cs.conditions, name)
211 }
212 }
213}
214
215// Remove changes the set to delete `conditions`.
216func (cs *LicenseConditionSet) Remove(conditions ...LicenseCondition) {
217 for _, lc := range conditions {
218 if _, isPresent := cs.conditions[lc.name]; !isPresent {
219 panic(fmt.Errorf("attempt to remove non-existent condition: %q", lc.asString(":")))
220 }
221 if _, isPresent := cs.conditions[lc.name][lc.origin]; !isPresent {
222 panic(fmt.Errorf("attempt to remove non-existent origin: %q", lc.asString(":")))
223 }
224 delete(cs.conditions[lc.name], lc.origin)
225 }
226}
227
228// removeSet changes the set to delete all conditions also present in `other`.
229func (cs *LicenseConditionSet) RemoveSet(other *LicenseConditionSet) {
230 for name, origins := range other.conditions {
231 if _, isPresent := cs.conditions[name]; !isPresent {
232 continue
233 }
234 for origin := range origins {
235 delete(cs.conditions[name], origin)
236 }
237 }
238}
239
240// compliance-only LicenseConditionSet methods
241
242// newLicenseConditionSet constructs a set of `conditions`.
243func newLicenseConditionSet() *LicenseConditionSet {
244 return &LicenseConditionSet{make(map[string]map[*TargetNode]bool)}
245}
246
247// add changes the set to include each element of `conditions` originating at `origin`.
248func (cs *LicenseConditionSet) add(origin *TargetNode, conditions ...string) {
249 for _, name := range conditions {
250 if _, ok := cs.conditions[name]; !ok {
251 cs.conditions[name] = make(map[*TargetNode]bool)
252 }
253 cs.conditions[name][origin] = true
254 }
255}
256
257// asStringList returns the conditions in the set as `separator`-separated (origin, condition-name) pair strings.
258func (cs *LicenseConditionSet) asStringList(separator string) []string {
259 result := make([]string, 0, cs.Count())
260 for name, origins := range cs.conditions {
261 for origin := range origins {
262 result = append(result, origin.name+separator+name)
263 }
264 }
265 return result
266}
267
268// conditionNamesArray implements a `contains` predicate for arrays of ConditionNames
269type conditionNamesArray []ConditionNames
270
271func (cn conditionNamesArray) contains(name string) bool {
272 for _, names := range cn {
273 if names.Contains(name) {
274 return true
275 }
276 }
277 return false
278}