yq/pkg/yqlib/treeops/data_tree_navigator.go

165 lines
4.6 KiB
Go

package treeops
import (
"github.com/elliotchance/orderedmap"
)
type dataTreeNavigator struct {
traverser Traverser
}
type NavigationPrefs struct {
FollowAlias bool
}
type DataTreeNavigator interface {
GetMatchingNodes(matchingNodes []*CandidateNode, pathNode *PathTreeNode) ([]*CandidateNode, error)
}
func NewDataTreeNavigator(navigationPrefs NavigationPrefs) DataTreeNavigator {
traverse := NewTraverser(navigationPrefs)
return &dataTreeNavigator{traverse}
}
func (d *dataTreeNavigator) traverse(matchMap *orderedmap.OrderedMap, pathNode *PathElement) (*orderedmap.OrderedMap, error) {
log.Debugf("-- Traversing")
var matchingNodeMap = orderedmap.NewOrderedMap()
var newNodes []*CandidateNode
var err error
for el := matchMap.Front(); el != nil; el = el.Next() {
newNodes, err = d.traverser.Traverse(el.Value.(*CandidateNode), pathNode)
if err != nil {
return nil, err
}
for _, n := range newNodes {
matchingNodeMap.Set(n.getKey(), n)
}
}
return matchingNodeMap, nil
}
func (d *dataTreeNavigator) equalsOperation(matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
log.Debugf("-- equalsOperation")
var results = orderedmap.NewOrderedMap()
for el := matchMap.Front(); el != nil; el = el.Next() {
elMap := orderedmap.NewOrderedMap()
elMap.Set(el.Key, el.Value)
//need to splat matching nodes, then search through them
splatter := &PathTreeNode{PathElement: &PathElement{
PathElementType: PathKey,
Value: "*",
StringValue: "*",
}}
children, err := d.getMatchingNodes(elMap, splatter)
log.Debugf("-- splatted matches, ")
if err != nil {
return nil, err
}
for childEl := children.Front(); childEl != nil; childEl = childEl.Next() {
childMap := orderedmap.NewOrderedMap()
childMap.Set(childEl.Key, childEl.Value)
childMatches, errChild := d.getMatchingNodes(childMap, pathNode.Lhs)
if errChild != nil {
return nil, errChild
}
if d.containsMatchingValue(childMatches, pathNode.Rhs.PathElement.StringValue) {
results.Set(childEl.Key, childEl.Value)
}
}
}
return results, nil
}
func (d *dataTreeNavigator) containsMatchingValue(matchMap *orderedmap.OrderedMap, valuePattern string) bool {
log.Debugf("-- findMatchingValues")
for el := matchMap.Front(); el != nil; el = el.Next() {
node := el.Value.(*CandidateNode)
if Match(node.Node.Value, valuePattern) {
return true
}
}
return false
}
func (d *dataTreeNavigator) setFunction(op OperationType, lhs *orderedmap.OrderedMap, rhs *orderedmap.OrderedMap) *orderedmap.OrderedMap {
if op == Or {
for el := rhs.Front(); el != nil; el = el.Next() {
node := el.Value.(*CandidateNode)
lhs.Set(node.getKey(), node)
}
return lhs
}
var matchingNodeMap = orderedmap.NewOrderedMap()
for el := lhs.Front(); el != nil; el = el.Next() {
_, exists := rhs.Get(el.Key)
if exists {
matchingNodeMap.Set(el.Key, el.Value)
}
}
return matchingNodeMap
}
func (d *dataTreeNavigator) GetMatchingNodes(matchingNodes []*CandidateNode, pathNode *PathTreeNode) ([]*CandidateNode, error) {
var matchingNodeMap = orderedmap.NewOrderedMap()
for _, n := range matchingNodes {
matchingNodeMap.Set(n.getKey(), n)
}
matchedNodes, err := d.getMatchingNodes(matchingNodeMap, pathNode)
if err != nil {
return nil, err
}
values := make([]*CandidateNode, 0, matchedNodes.Len())
for el := matchedNodes.Front(); el != nil; el = el.Next() {
values = append(values, el.Value.(*CandidateNode))
}
return values, nil
}
func (d *dataTreeNavigator) getMatchingNodes(matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
log.Debugf("Processing Path: %v", pathNode.PathElement.toString())
if pathNode.PathElement.PathElementType == PathKey || pathNode.PathElement.PathElementType == ArrayIndex {
return d.traverse(matchingNodes, pathNode.PathElement)
} else {
var lhs, rhs *orderedmap.OrderedMap
var err error
switch pathNode.PathElement.OperationType {
case Traverse:
lhs, err = d.getMatchingNodes(matchingNodes, pathNode.Lhs)
if err != nil {
return nil, err
}
return d.getMatchingNodes(lhs, pathNode.Rhs)
case Or, And:
lhs, err = d.getMatchingNodes(matchingNodes, pathNode.Lhs)
if err != nil {
return nil, err
}
rhs, err = d.getMatchingNodes(matchingNodes, pathNode.Rhs)
if err != nil {
return nil, err
}
return d.setFunction(pathNode.PathElement.OperationType, lhs, rhs), nil
case Equals:
return d.equalsOperation(matchingNodes, pathNode)
// case EqualsSelf:
// return d.findMatchingValues(matchingNodes, pathNode.Rhs)
default:
return nil, nil
}
}
}