compliance package policy and resolves

package to read, consume, and analyze license metadata and dependency
graph.

Bug: 68860345
Bug: 151177513
Bug: 151953481

Change-Id: Ic08406fa2250a08ad26f2167d934f841c95d9148
diff --git a/tools/compliance/policy/walk.go b/tools/compliance/policy/walk.go
new file mode 100644
index 0000000..8b6737d
--- /dev/null
+++ b/tools/compliance/policy/walk.go
@@ -0,0 +1,76 @@
+// 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
+
+// VisitNode is called for each root and for each walked dependency node by
+// WalkTopDown. When VisitNode returns true, WalkTopDown will proceed to walk
+// down the dependences of the node
+type VisitNode func(*LicenseGraph, *TargetNode, TargetEdgePath) bool
+
+// WalkTopDown does a top-down walk of `lg` calling `visit` and descending
+// into depenencies when `visit` returns true.
+func WalkTopDown(lg *LicenseGraph, visit VisitNode) {
+	path := NewTargetEdgePath(32)
+
+	// must be indexed for fast lookup
+	lg.indexForward()
+
+	var walk func(f string)
+	walk = func(f string) {
+		visitChildren := visit(lg, lg.targets[f], *path)
+		if !visitChildren {
+			return
+		}
+		for _, edge := range lg.index[f] {
+			path.Push(TargetEdge{lg, edge})
+			walk(edge.dependency)
+			path.Pop()
+		}
+	}
+
+	for _, r := range lg.rootFiles {
+		path.Clear()
+		walk(r)
+	}
+}
+
+// WalkResolutionsForCondition performs a top-down walk of the LicenseGraph
+// resolving all distributed works for condition `names`.
+func WalkResolutionsForCondition(lg *LicenseGraph, rs *ResolutionSet, names ConditionNames) *ResolutionSet {
+	shipped := ShippedNodes(lg)
+
+	// rmap maps 'attachesTo' targets to the `actsOn` targets and applicable conditions
+	//
+	// rmap is the resulting ResolutionSet
+	rmap := make(map[*TargetNode]actionSet)
+
+	WalkTopDown(lg, func(lg *LicenseGraph, tn *TargetNode, _ TargetEdgePath) bool {
+		if _, ok := rmap[tn]; ok {
+			return false
+		}
+		if !shipped.Contains(tn) {
+			return false
+		}
+		if as, ok := rs.resolutions[tn]; ok {
+			fas := as.byActsOn(shipped).byName(names)
+			if !fas.isEmpty() {
+				rmap[tn] = fas
+			}
+		}
+		return tn.IsContainer() // descend into containers
+	})
+
+	return &ResolutionSet{rmap}
+}