209 lines
6 KiB
Go
209 lines
6 KiB
Go
|
// 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.Copy()
|
||
|
}
|
||
|
|
||
|
// asString returns a string representation of the resolution.
|
||
|
func (r Resolution) asString() string {
|
||
|
var sb strings.Builder
|
||
|
cl := r.cs.AsList()
|
||
|
sort.Sort(cl)
|
||
|
fmt.Fprintf(&sb, "%s -> %s -> %s", r.attachesTo.name, r.actsOn.name, cl.String())
|
||
|
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.AddSet(r.cs)
|
||
|
}
|
||
|
return result
|
||
|
}
|
||
|
|
||
|
// ByName returns the sub-list of resolutions resolving conditions matching
|
||
|
// `names`.
|
||
|
func (rl ResolutionList) ByName(names ConditionNames) ResolutionList {
|
||
|
result := make(ResolutionList, 0, rl.CountByName(names))
|
||
|
for _, r := range rl {
|
||
|
if r.Resolves().HasAnyByName(names) {
|
||
|
result = append(result, Resolution{r.attachesTo, r.actsOn, r.cs.ByName(names)})
|
||
|
}
|
||
|
}
|
||
|
return result
|
||
|
}
|
||
|
|
||
|
// CountByName returns the number of resolutions resolving conditions matching
|
||
|
// `names`.
|
||
|
func (rl ResolutionList) CountByName(names ConditionNames) int {
|
||
|
c := 0
|
||
|
for _, r := range rl {
|
||
|
if r.Resolves().HasAnyByName(names) {
|
||
|
c++
|
||
|
}
|
||
|
}
|
||
|
return c
|
||
|
}
|
||
|
|
||
|
// CountConditionsByName returns a count of distinct resolution/conditions
|
||
|
// pairs matching `names`.
|
||
|
//
|
||
|
// A single resolution might resolve multiple conditions matching `names`.
|
||
|
func (rl ResolutionList) CountConditionsByName(names ConditionNames) int {
|
||
|
c := 0
|
||
|
for _, r := range rl {
|
||
|
c += r.Resolves().CountByName(names)
|
||
|
}
|
||
|
return c
|
||
|
}
|
||
|
|
||
|
// ByAttachesTo returns the sub-list of resolutions attached to `attachesTo`.
|
||
|
func (rl ResolutionList) ByAttachesTo(attachesTo *TargetNode) ResolutionList {
|
||
|
result := make(ResolutionList, 0, rl.CountByActsOn(attachesTo))
|
||
|
for _, r := range rl {
|
||
|
if r.attachesTo == attachesTo {
|
||
|
result = append(result, r)
|
||
|
}
|
||
|
}
|
||
|
return result
|
||
|
}
|
||
|
|
||
|
// CountByAttachesTo returns the number of resolutions attached to
|
||
|
// `attachesTo`.
|
||
|
func (rl ResolutionList) CountByAttachesTo(attachesTo *TargetNode) int {
|
||
|
c := 0
|
||
|
for _, r := range rl {
|
||
|
if r.attachesTo == attachesTo {
|
||
|
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
|
||
|
}
|
||
|
|
||
|
// ByOrigin returns the sub-list of resolutions resolving license conditions
|
||
|
// originating at `origin`.
|
||
|
func (rl ResolutionList) ByOrigin(origin *TargetNode) ResolutionList {
|
||
|
result := make(ResolutionList, 0, rl.CountByOrigin(origin))
|
||
|
for _, r := range rl {
|
||
|
if r.Resolves().HasAnyByOrigin(origin) {
|
||
|
result = append(result, Resolution{r.attachesTo, r.actsOn, r.cs.ByOrigin(origin)})
|
||
|
}
|
||
|
}
|
||
|
return result
|
||
|
}
|
||
|
|
||
|
// CountByOrigin returns the number of resolutions resolving license conditions
|
||
|
// originating at `origin`.
|
||
|
func (rl ResolutionList) CountByOrigin(origin *TargetNode) int {
|
||
|
c := 0
|
||
|
for _, r := range rl {
|
||
|
if r.Resolves().HasAnyByOrigin(origin) {
|
||
|
c++
|
||
|
}
|
||
|
}
|
||
|
return c
|
||
|
}
|