1// 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 53} 54 55// asString returns a string representation of the resolution. 56func (r Resolution) asString() string { 57 var sb strings.Builder 58 names := r.cs.Names() 59 sort.Strings(names) 60 fmt.Fprintf(&sb, "%s -> %s{%s}", r.attachesTo.name, r.actsOn.name, strings.Join(names, ", ")) 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 = result.Union(r.cs) 101 } 102 return result 103} 104 105// ByName returns the sub-list of resolutions resolving conditions matching 106// `names`. 107func (rl ResolutionList) Matching(conditions LicenseConditionSet) ResolutionList { 108 result := make(ResolutionList, 0, rl.CountMatching(conditions)) 109 for _, r := range rl { 110 if r.Resolves().MatchesAnySet(conditions) { 111 result = append(result, Resolution{r.attachesTo, r.actsOn, r.cs.MatchingAnySet(conditions)}) 112 } 113 } 114 return result 115} 116 117// CountMatching returns the number of resolutions resolving conditions matching 118// `conditions`. 119func (rl ResolutionList) CountMatching(conditions LicenseConditionSet) int { 120 c := 0 121 for _, r := range rl { 122 if r.Resolves().MatchesAnySet(conditions) { 123 c++ 124 } 125 } 126 return c 127} 128 129// ByActsOn returns the sub-list of resolutions matching `actsOn`. 130func (rl ResolutionList) ByActsOn(actsOn *TargetNode) ResolutionList { 131 result := make(ResolutionList, 0, rl.CountByActsOn(actsOn)) 132 for _, r := range rl { 133 if r.actsOn == actsOn { 134 result = append(result, r) 135 } 136 } 137 return result 138} 139 140// CountByActsOn returns the number of resolutions matching `actsOn`. 141func (rl ResolutionList) CountByActsOn(actsOn *TargetNode) int { 142 c := 0 143 for _, r := range rl { 144 if r.actsOn == actsOn { 145 c++ 146 } 147 } 148 return c 149} 150