1package xpath
2
3import (
4	"errors"
5)
6
7// NodeType represents a type of XPath node.
8type NodeType int
9
10const (
11	// RootNode is a root node of the XML document or node tree.
12	RootNode NodeType = iota
13
14	// ElementNode is an element, such as <element>.
15	ElementNode
16
17	// AttributeNode is an attribute, such as id='123'.
18	AttributeNode
19
20	// TextNode is the text content of a node.
21	TextNode
22
23	// CommentNode is a comment node, such as <!-- my comment -->
24	CommentNode
25
26	// allNode is any types of node, used by xpath package only to predicate match.
27	allNode
28)
29
30// NodeNavigator provides cursor model for navigating XML data.
31type NodeNavigator interface {
32	// NodeType returns the XPathNodeType of the current node.
33	NodeType() NodeType
34
35	// LocalName gets the Name of the current node.
36	LocalName() string
37
38	// Prefix returns namespace prefix associated with the current node.
39	Prefix() string
40
41	// Value gets the value of current node.
42	Value() string
43
44	// Copy does a deep copy of the NodeNavigator and all its components.
45	Copy() NodeNavigator
46
47	// MoveToRoot moves the NodeNavigator to the root node of the current node.
48	MoveToRoot()
49
50	// MoveToParent moves the NodeNavigator to the parent node of the current node.
51	MoveToParent() bool
52
53	// MoveToNextAttribute moves the NodeNavigator to the next attribute on current node.
54	MoveToNextAttribute() bool
55
56	// MoveToChild moves the NodeNavigator to the first child node of the current node.
57	MoveToChild() bool
58
59	// MoveToFirst moves the NodeNavigator to the first sibling node of the current node.
60	MoveToFirst() bool
61
62	// MoveToNext moves the NodeNavigator to the next sibling node of the current node.
63	MoveToNext() bool
64
65	// MoveToPrevious moves the NodeNavigator to the previous sibling node of the current node.
66	MoveToPrevious() bool
67
68	// MoveTo moves the NodeNavigator to the same position as the specified NodeNavigator.
69	MoveTo(NodeNavigator) bool
70}
71
72// NodeIterator holds all matched Node object.
73type NodeIterator struct {
74	node  NodeNavigator
75	query query
76}
77
78// Current returns current node which matched.
79func (t *NodeIterator) Current() NodeNavigator {
80	return t.node
81}
82
83// MoveNext moves Navigator to the next match node.
84func (t *NodeIterator) MoveNext() bool {
85	n := t.query.Select(t)
86	if n != nil {
87		if !t.node.MoveTo(n) {
88			t.node = n.Copy()
89		}
90		return true
91	}
92	return false
93}
94
95// Select selects a node set using the specified XPath expression.
96// This method is deprecated, recommend using Expr.Select() method instead.
97func Select(root NodeNavigator, expr string) *NodeIterator {
98	exp, err := Compile(expr)
99	if err != nil {
100		panic(err)
101	}
102	return exp.Select(root)
103}
104
105// Expr is an XPath expression for query.
106type Expr struct {
107	s string
108	q query
109}
110
111type iteratorFunc func() NodeNavigator
112
113func (f iteratorFunc) Current() NodeNavigator {
114	return f()
115}
116
117// Evaluate returns the result of the expression.
118// The result type of the expression is one of the follow: bool,float64,string,NodeIterator).
119func (expr *Expr) Evaluate(root NodeNavigator) interface{} {
120	val := expr.q.Evaluate(iteratorFunc(func() NodeNavigator { return root }))
121	switch val.(type) {
122	case query:
123		return &NodeIterator{query: expr.q.Clone(), node: root}
124	}
125	return val
126}
127
128// Select selects a node set using the specified XPath expression.
129func (expr *Expr) Select(root NodeNavigator) *NodeIterator {
130	return &NodeIterator{query: expr.q.Clone(), node: root}
131}
132
133// String returns XPath expression string.
134func (expr *Expr) String() string {
135	return expr.s
136}
137
138// Compile compiles an XPath expression string.
139func Compile(expr string) (*Expr, error) {
140	if expr == "" {
141		return nil, errors.New("expr expression is nil")
142	}
143	qy, err := build(expr)
144	if err != nil {
145		return nil, err
146	}
147	return &Expr{s: expr, q: qy}, nil
148}
149
150// MustCompile compiles an XPath expression string and ignored error.
151func MustCompile(expr string) *Expr {
152	exp, err := Compile(expr)
153	if err != nil {
154		return nil
155	}
156	return exp
157}
158