308 lines
6.0 KiB
Go
308 lines
6.0 KiB
Go
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
|
|
}
|