compliance package structures for license metadata
package to read, consume, and analyze license metadata and dependency
graph.
Bug: 68860345
Bug: 151177513
Bug: 151953481
Change-Id: I3ebf44e4d5195b9851fd076161049bf82ed76dd2
diff --git a/tools/compliance/conditionset.go b/tools/compliance/conditionset.go
new file mode 100644
index 0000000..d972eb9
--- /dev/null
+++ b/tools/compliance/conditionset.go
@@ -0,0 +1,269 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "fmt"
+)
+
+// NewLicenseConditionSet creates a new instance or variable of *LicenseConditionSet.
+func NewLicenseConditionSet(conditions ...LicenseCondition) *LicenseConditionSet {
+ cs := newLicenseConditionSet()
+ cs.Add(conditions...)
+ return cs
+}
+
+// LicenseConditionSet describes a mutable set of immutable license conditions.
+type LicenseConditionSet struct {
+ // conditions describes the set of license conditions i.e. (condition name, origin target) pairs
+ // by mapping condition name -> origin target -> true.
+ conditions map[string]map[*TargetNode]bool
+}
+
+// Add makes all `conditions` members of the set if they were not previously.
+func (cs *LicenseConditionSet) Add(conditions ...LicenseCondition) {
+ if len(conditions) == 0 {
+ return
+ }
+ for _, lc := range conditions {
+ if _, ok := cs.conditions[lc.name]; !ok {
+ cs.conditions[lc.name] = make(map[*TargetNode]bool)
+ }
+ cs.conditions[lc.name][lc.origin] = true
+ }
+}
+
+// AddSet makes all elements of `conditions` members of the set if they were not previously.
+func (cs *LicenseConditionSet) AddSet(other *LicenseConditionSet) {
+ if len(other.conditions) == 0 {
+ return
+ }
+ for name, origins := range other.conditions {
+ if len(origins) == 0 {
+ continue
+ }
+ if _, ok := cs.conditions[name]; !ok {
+ cs.conditions[name] = make(map[*TargetNode]bool)
+ }
+ for origin := range origins {
+ cs.conditions[name][origin] = other.conditions[name][origin]
+ }
+ }
+}
+
+// ByName returns a list of the conditions in the set matching `names`.
+func (cs *LicenseConditionSet) ByName(names ...ConditionNames) *LicenseConditionSet {
+ other := newLicenseConditionSet()
+ for _, cn := range names {
+ for _, name := range cn {
+ if origins, ok := cs.conditions[name]; ok {
+ other.conditions[name] = make(map[*TargetNode]bool)
+ for origin := range origins {
+ other.conditions[name][origin] = true
+ }
+ }
+ }
+ }
+ return other
+}
+
+// HasAnyByName returns true if the set contains any conditions matching `names` originating at any target.
+func (cs *LicenseConditionSet) HasAnyByName(names ...ConditionNames) bool {
+ for _, cn := range names {
+ for _, name := range cn {
+ if origins, ok := cs.conditions[name]; ok {
+ if len(origins) > 0 {
+ return true
+ }
+ }
+ }
+ }
+ return false
+}
+
+// CountByName returns the number of conditions matching `names` originating at any target.
+func (cs *LicenseConditionSet) CountByName(names ...ConditionNames) int {
+ size := 0
+ for _, cn := range names {
+ for _, name := range cn {
+ if origins, ok := cs.conditions[name]; ok {
+ size += len(origins)
+ }
+ }
+ }
+ return size
+}
+
+// ByOrigin returns all of the conditions that originate at `origin` regardless of name.
+func (cs *LicenseConditionSet) ByOrigin(origin *TargetNode) *LicenseConditionSet {
+ other := newLicenseConditionSet()
+ for name, origins := range cs.conditions {
+ if _, ok := origins[origin]; ok {
+ other.conditions[name] = make(map[*TargetNode]bool)
+ other.conditions[name][origin] = true
+ }
+ }
+ return other
+}
+
+// HasAnyByOrigin returns true if the set contains any conditions originating at `origin` regardless of condition name.
+func (cs *LicenseConditionSet) HasAnyByOrigin(origin *TargetNode) bool {
+ for _, origins := range cs.conditions {
+ if _, ok := origins[origin]; ok {
+ return true
+ }
+ }
+ return false
+}
+
+// CountByOrigin returns the number of conditions originating at `origin` regardless of condition name.
+func (cs *LicenseConditionSet) CountByOrigin(origin *TargetNode) int {
+ size := 0
+ for _, origins := range cs.conditions {
+ if _, ok := origins[origin]; ok {
+ size++
+ }
+ }
+ return size
+}
+
+// AsList returns a list of all the conditions in the set.
+func (cs *LicenseConditionSet) AsList() ConditionList {
+ result := make(ConditionList, 0, cs.Count())
+ for name, origins := range cs.conditions {
+ for origin := range origins {
+ result = append(result, LicenseCondition{name, origin})
+ }
+ }
+ return result
+}
+
+// Count returns the number of conditions in the set.
+func (cs *LicenseConditionSet) Count() int {
+ size := 0
+ for _, origins := range cs.conditions {
+ size += len(origins)
+ }
+ return size
+}
+
+// Copy creates a new LicenseCondition variable with the same value.
+func (cs *LicenseConditionSet) Copy() *LicenseConditionSet {
+ other := newLicenseConditionSet()
+ for name := range cs.conditions {
+ other.conditions[name] = make(map[*TargetNode]bool)
+ for origin := range cs.conditions[name] {
+ other.conditions[name][origin] = cs.conditions[name][origin]
+ }
+ }
+ return other
+}
+
+// HasCondition returns true if the set contains any condition matching both `names` and `origin`.
+func (cs *LicenseConditionSet) HasCondition(names ConditionNames, origin *TargetNode) bool {
+ for _, name := range names {
+ if origins, ok := cs.conditions[name]; ok {
+ _, isPresent := origins[origin]
+ if isPresent {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+// IsEmpty returns true when the set of conditions contains zero elements.
+func (cs *LicenseConditionSet) IsEmpty() bool {
+ for _, origins := range cs.conditions {
+ if 0 < len(origins) {
+ return false
+ }
+ }
+ return true
+}
+
+// RemoveAllByName changes the set to delete all conditions matching `names`.
+func (cs *LicenseConditionSet) RemoveAllByName(names ...ConditionNames) {
+ for _, cn := range names {
+ for _, name := range cn {
+ delete(cs.conditions, name)
+ }
+ }
+}
+
+// Remove changes the set to delete `conditions`.
+func (cs *LicenseConditionSet) Remove(conditions ...LicenseCondition) {
+ for _, lc := range conditions {
+ if _, isPresent := cs.conditions[lc.name]; !isPresent {
+ panic(fmt.Errorf("attempt to remove non-existent condition: %q", lc.asString(":")))
+ }
+ if _, isPresent := cs.conditions[lc.name][lc.origin]; !isPresent {
+ panic(fmt.Errorf("attempt to remove non-existent origin: %q", lc.asString(":")))
+ }
+ delete(cs.conditions[lc.name], lc.origin)
+ }
+}
+
+// removeSet changes the set to delete all conditions also present in `other`.
+func (cs *LicenseConditionSet) RemoveSet(other *LicenseConditionSet) {
+ for name, origins := range other.conditions {
+ if _, isPresent := cs.conditions[name]; !isPresent {
+ continue
+ }
+ for origin := range origins {
+ delete(cs.conditions[name], origin)
+ }
+ }
+}
+
+// compliance-only LicenseConditionSet methods
+
+// newLicenseConditionSet constructs a set of `conditions`.
+func newLicenseConditionSet() *LicenseConditionSet {
+ return &LicenseConditionSet{make(map[string]map[*TargetNode]bool)}
+}
+
+// add changes the set to include each element of `conditions` originating at `origin`.
+func (cs *LicenseConditionSet) add(origin *TargetNode, conditions ...string) {
+ for _, name := range conditions {
+ if _, ok := cs.conditions[name]; !ok {
+ cs.conditions[name] = make(map[*TargetNode]bool)
+ }
+ cs.conditions[name][origin] = true
+ }
+}
+
+// asStringList returns the conditions in the set as `separator`-separated (origin, condition-name) pair strings.
+func (cs *LicenseConditionSet) asStringList(separator string) []string {
+ result := make([]string, 0, cs.Count())
+ for name, origins := range cs.conditions {
+ for origin := range origins {
+ result = append(result, origin.name+separator+name)
+ }
+ }
+ return result
+}
+
+// conditionNamesArray implements a `contains` predicate for arrays of ConditionNames
+type conditionNamesArray []ConditionNames
+
+func (cn conditionNamesArray) contains(name string) bool {
+ for _, names := range cn {
+ if names.Contains(name) {
+ return true
+ }
+ }
+ return false
+}