-
-
Notifications
You must be signed in to change notification settings - Fork 76
/
Copy pathquery.go
96 lines (87 loc) · 2.69 KB
/
query.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package idr
import (
"errors"
"fmt"
"github.com/antchfx/xpath"
"github.com/jf-tech/go-corelib/caches"
)
var (
// ErrNoMatch is returned when not a single matched node can be found.
ErrNoMatch = errors.New("no match")
// ErrMoreThanExpected is returned when more than expected matched nodes are found.
ErrMoreThanExpected = errors.New("more than expected matched")
)
const (
// DisableXPathCache disables caching xpath compilation when MatchAll/MatchSingle
// are called. Useful when caller knows the xpath string isn't cache-able (such as
// containing unique IDs, timestamps, etc.) which would otherwise cause the xpath
// compilation cache grow unbounded.
DisableXPathCache = uint(1) << iota
)
func loadXPathExpr(exprStr string, flags []uint) (*xpath.Expr, error) {
var flagsActual uint
switch len(flags) {
case 0:
flagsActual = 0
case 1:
flagsActual = flags[0]
default:
return nil, fmt.Errorf("only one flag is allowed, instead got: %d", len(flags))
}
var expr interface{}
var err error
if flagsActual&DisableXPathCache != 0 {
expr, err = xpath.Compile(exprStr)
} else {
expr, err = caches.GetXPathExpr(exprStr)
}
if err != nil {
return nil, fmt.Errorf("xpath '%s' compilation failed: %s", exprStr, err.Error())
}
return expr.(*xpath.Expr), nil
}
// QueryIter initiates an xpath query specified by 'expr' against an IDR tree rooted at 'n'.
func QueryIter(n *Node, expr *xpath.Expr) *xpath.NodeIterator {
return expr.Select(createNavigator(n))
}
// MatchAny returns true if the xpath query 'expr' against an IDR tree rooted at 'n' yields any result.
func MatchAny(n *Node, expr *xpath.Expr) bool {
return QueryIter(n, expr).MoveNext()
}
// MatchAll returns all the matched nodes by an xpath query 'exprStr' against an IDR tree rooted at 'n'.
func MatchAll(n *Node, exprStr string, flags ...uint) ([]*Node, error) {
if exprStr == "." {
return []*Node{n}, nil
}
exp, err := loadXPathExpr(exprStr, flags)
if err != nil {
return nil, err
}
iter := QueryIter(n, exp)
var ret []*Node
for iter.MoveNext() {
ret = append(ret, nodeFromIter(iter))
}
return ret, nil
}
// MatchSingle returns one and only one matched node by an xpath query 'exprStr' against an IDR tree rooted
// at 'n'. If no matching node is found, ErrNoMatch is returned; if more than one matching nodes are found,
// ErrMoreThanExpected is returned.
func MatchSingle(n *Node, exprStr string, flags ...uint) (*Node, error) {
if exprStr == "." {
return n, nil
}
expr, err := loadXPathExpr(exprStr, flags)
if err != nil {
return nil, err
}
iter := QueryIter(n, expr)
if !iter.MoveNext() {
return nil, ErrNoMatch
}
ret := nodeFromIter(iter)
if iter.MoveNext() {
return nil, ErrMoreThanExpected
}
return ret, nil
}