| // 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" |
| "sort" |
| "strings" |
| ) |
| |
| // Resolution describes an action to resolve one or more license conditions. |
| // |
| // `AttachesTo` identifies the target node that when distributed triggers the action. |
| // `ActsOn` identifies the target node that is the object of the action. |
| // `Resolves` identifies one or more license conditions that the action resolves. |
| // |
| // e.g. Suppose an MIT library is linked to a binary that also links to GPL code. |
| // |
| // A resolution would attach to the binary to share (act on) the MIT library to |
| // resolve the restricted condition originating from the GPL code. |
| type Resolution struct { |
| attachesTo, actsOn *TargetNode |
| cs LicenseConditionSet |
| } |
| |
| // AttachesTo returns the target node the resolution attaches to. |
| func (r Resolution) AttachesTo() *TargetNode { |
| return r.attachesTo |
| } |
| |
| // ActsOn returns the target node that must be acted on to resolve the condition. |
| // |
| // i.e. The node for which notice must be given or whose source must be shared etc. |
| func (r Resolution) ActsOn() *TargetNode { |
| return r.actsOn |
| } |
| |
| // Resolves returns the set of license condition the resolution satisfies. |
| func (r Resolution) Resolves() LicenseConditionSet { |
| return r.cs |
| } |
| |
| // asString returns a string representation of the resolution. |
| func (r Resolution) asString() string { |
| var sb strings.Builder |
| names := r.cs.Names() |
| sort.Strings(names) |
| fmt.Fprintf(&sb, "%s -> %s{%s}", r.attachesTo.name, r.actsOn.name, strings.Join(names, ", ")) |
| return sb.String() |
| } |
| |
| // ResolutionList represents a partial order of Resolutions ordered by |
| // AttachesTo() and ActsOn() leaving `Resolves()` unordered. |
| type ResolutionList []Resolution |
| |
| // Len returns the count of elements in the list. |
| func (l ResolutionList) Len() int { return len(l) } |
| |
| // Swap rearranges 2 elements so that each occupies the other's former position. |
| func (l ResolutionList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } |
| |
| // Less returns true when the `i`th element is lexicographically less than tht `j`th. |
| func (l ResolutionList) Less(i, j int) bool { |
| if l[i].attachesTo.name == l[j].attachesTo.name { |
| return l[i].actsOn.name < l[j].actsOn.name |
| } |
| return l[i].attachesTo.name < l[j].attachesTo.name |
| } |
| |
| // String returns a string representation of the list. |
| func (rl ResolutionList) String() string { |
| var sb strings.Builder |
| fmt.Fprintf(&sb, "[") |
| sep := "" |
| for _, r := range rl { |
| fmt.Fprintf(&sb, "%s%s", sep, r.asString()) |
| sep = ", " |
| } |
| fmt.Fprintf(&sb, "]") |
| return sb.String() |
| } |
| |
| // AllConditions returns the union of all license conditions resolved by any |
| // element of the list. |
| func (rl ResolutionList) AllConditions() LicenseConditionSet { |
| result := NewLicenseConditionSet() |
| for _, r := range rl { |
| result = result.Union(r.cs) |
| } |
| return result |
| } |
| |
| // ByName returns the sub-list of resolutions resolving conditions matching |
| // `names`. |
| func (rl ResolutionList) Matching(conditions LicenseConditionSet) ResolutionList { |
| result := make(ResolutionList, 0, rl.CountMatching(conditions)) |
| for _, r := range rl { |
| if r.Resolves().MatchesAnySet(conditions) { |
| result = append(result, Resolution{r.attachesTo, r.actsOn, r.cs.MatchingAnySet(conditions)}) |
| } |
| } |
| return result |
| } |
| |
| // CountMatching returns the number of resolutions resolving conditions matching |
| // `conditions`. |
| func (rl ResolutionList) CountMatching(conditions LicenseConditionSet) int { |
| c := 0 |
| for _, r := range rl { |
| if r.Resolves().MatchesAnySet(conditions) { |
| c++ |
| } |
| } |
| return c |
| } |
| |
| // ByActsOn returns the sub-list of resolutions matching `actsOn`. |
| func (rl ResolutionList) ByActsOn(actsOn *TargetNode) ResolutionList { |
| result := make(ResolutionList, 0, rl.CountByActsOn(actsOn)) |
| for _, r := range rl { |
| if r.actsOn == actsOn { |
| result = append(result, r) |
| } |
| } |
| return result |
| } |
| |
| // CountByActsOn returns the number of resolutions matching `actsOn`. |
| func (rl ResolutionList) CountByActsOn(actsOn *TargetNode) int { |
| c := 0 |
| for _, r := range rl { |
| if r.actsOn == actsOn { |
| c++ |
| } |
| } |
| return c |
| } |