package findutil import ( "encoding/xml" "github.com/ChrisTrenkamp/goxpath/parser/pathexpr" "github.com/ChrisTrenkamp/goxpath/tree" "github.com/ChrisTrenkamp/goxpath/xconst" ) const ( wildcard = "*" ) type findFunc func(tree.Node, *pathexpr.PathExpr, *[]tree.Node) var findMap = map[string]findFunc{ xconst.AxisAncestor: findAncestor, xconst.AxisAncestorOrSelf: findAncestorOrSelf, xconst.AxisAttribute: findAttribute, xconst.AxisChild: findChild, xconst.AxisDescendent: findDescendent, xconst.AxisDescendentOrSelf: findDescendentOrSelf, xconst.AxisFollowing: findFollowing, xconst.AxisFollowingSibling: findFollowingSibling, xconst.AxisNamespace: findNamespace, xconst.AxisParent: findParent, xconst.AxisPreceding: findPreceding, xconst.AxisPrecedingSibling: findPrecedingSibling, xconst.AxisSelf: findSelf, } //Find finds nodes based on the pathexpr.PathExpr func Find(x tree.Node, p pathexpr.PathExpr) []tree.Node { ret := []tree.Node{} if p.Axis == "" { findChild(x, &p, &ret) return ret } f := findMap[p.Axis] f(x, &p, &ret) return ret } func findAncestor(x tree.Node, p *pathexpr.PathExpr, ret *[]tree.Node) { if x.GetNodeType() == tree.NtRoot { return } addNode(x.GetParent(), p, ret) findAncestor(x.GetParent(), p, ret) } func findAncestorOrSelf(x tree.Node, p *pathexpr.PathExpr, ret *[]tree.Node) { findSelf(x, p, ret) findAncestor(x, p, ret) } func findAttribute(x tree.Node, p *pathexpr.PathExpr, ret *[]tree.Node) { if ele, ok := x.(tree.Elem); ok { for _, i := range ele.GetAttrs() { addNode(i, p, ret) } } } func findChild(x tree.Node, p *pathexpr.PathExpr, ret *[]tree.Node) { if ele, ok := x.(tree.Elem); ok { ch := ele.GetChildren() for i := range ch { addNode(ch[i], p, ret) } } } func findDescendent(x tree.Node, p *pathexpr.PathExpr, ret *[]tree.Node) { if ele, ok := x.(tree.Elem); ok { ch := ele.GetChildren() for i := range ch { addNode(ch[i], p, ret) findDescendent(ch[i], p, ret) } } } func findDescendentOrSelf(x tree.Node, p *pathexpr.PathExpr, ret *[]tree.Node) { findSelf(x, p, ret) findDescendent(x, p, ret) } func findFollowing(x tree.Node, p *pathexpr.PathExpr, ret *[]tree.Node) { if x.GetNodeType() == tree.NtRoot { return } par := x.GetParent() ch := par.GetChildren() i := 0 for x != ch[i] { i++ } i++ for i < len(ch) { findDescendentOrSelf(ch[i], p, ret) i++ } findFollowing(par, p, ret) } func findFollowingSibling(x tree.Node, p *pathexpr.PathExpr, ret *[]tree.Node) { if x.GetNodeType() == tree.NtRoot { return } par := x.GetParent() ch := par.GetChildren() i := 0 for x != ch[i] { i++ } i++ for i < len(ch) { findSelf(ch[i], p, ret) i++ } } func findNamespace(x tree.Node, p *pathexpr.PathExpr, ret *[]tree.Node) { if ele, ok := x.(tree.NSElem); ok { ns := tree.BuildNS(ele) for _, i := range ns { addNode(i, p, ret) } } } func findParent(x tree.Node, p *pathexpr.PathExpr, ret *[]tree.Node) { if x.GetNodeType() != tree.NtRoot { addNode(x.GetParent(), p, ret) } } func findPreceding(x tree.Node, p *pathexpr.PathExpr, ret *[]tree.Node) { if x.GetNodeType() == tree.NtRoot { return } par := x.GetParent() ch := par.GetChildren() i := len(ch) - 1 for x != ch[i] { i-- } i-- for i >= 0 { findDescendentOrSelf(ch[i], p, ret) i-- } findPreceding(par, p, ret) } func findPrecedingSibling(x tree.Node, p *pathexpr.PathExpr, ret *[]tree.Node) { if x.GetNodeType() == tree.NtRoot { return } par := x.GetParent() ch := par.GetChildren() i := len(ch) - 1 for x != ch[i] { i-- } i-- for i >= 0 { findSelf(ch[i], p, ret) i-- } } func findSelf(x tree.Node, p *pathexpr.PathExpr, ret *[]tree.Node) { addNode(x, p, ret) } func addNode(x tree.Node, p *pathexpr.PathExpr, ret *[]tree.Node) { add := false tok := x.GetToken() switch x.GetNodeType() { case tree.NtAttr: add = evalAttr(p, tok.(xml.Attr)) case tree.NtChd: add = evalChd(p) case tree.NtComm: add = evalComm(p) case tree.NtElem, tree.NtRoot: add = evalEle(p, tok.(xml.StartElement)) case tree.NtNs: add = evalNS(p, tok.(xml.Attr)) case tree.NtPi: add = evalPI(p) } if add { *ret = append(*ret, x) } } func evalAttr(p *pathexpr.PathExpr, a xml.Attr) bool { if p.NodeType == "" { if p.Name.Space != wildcard { if a.Name.Space != p.NS[p.Name.Space] { return false } } if p.Name.Local == wildcard && p.Axis == xconst.AxisAttribute { return true } if p.Name.Local == a.Name.Local { return true } } else { if p.NodeType == xconst.NodeTypeNode { return true } } return false } func evalChd(p *pathexpr.PathExpr) bool { if p.NodeType == xconst.NodeTypeText || p.NodeType == xconst.NodeTypeNode { return true } return false } func evalComm(p *pathexpr.PathExpr) bool { if p.NodeType == xconst.NodeTypeComment || p.NodeType == xconst.NodeTypeNode { return true } return false } func evalEle(p *pathexpr.PathExpr, ele xml.StartElement) bool { if p.NodeType == "" { return checkNameAndSpace(p, ele) } if p.NodeType == xconst.NodeTypeNode { return true } return false } func checkNameAndSpace(p *pathexpr.PathExpr, ele xml.StartElement) bool { if p.Name.Local == wildcard && p.Name.Space == "" { return true } if p.Name.Space != wildcard && ele.Name.Space != p.NS[p.Name.Space] { return false } if p.Name.Local == wildcard && p.Axis != xconst.AxisAttribute && p.Axis != xconst.AxisNamespace { return true } if p.Name.Local == ele.Name.Local { return true } return false } func evalNS(p *pathexpr.PathExpr, ns xml.Attr) bool { if p.NodeType == "" { if p.Name.Space != "" && p.Name.Space != wildcard { return false } if p.Name.Local == wildcard && p.Axis == xconst.AxisNamespace { return true } if p.Name.Local == ns.Name.Local { return true } } else { if p.NodeType == xconst.NodeTypeNode { return true } } return false } func evalPI(p *pathexpr.PathExpr) bool { if p.NodeType == xconst.NodeTypeProcInst || p.NodeType == xconst.NodeTypeNode { return true } return false }