1package tree
2
3import (
4	"encoding/xml"
5	"sort"
6)
7
8//XMLSpace is the W3C XML namespace
9const XMLSpace = "http://www.w3.org/XML/1998/namespace"
10
11//NodePos is a helper for representing the node's document order
12type NodePos int
13
14//Pos returns the node's document order position
15func (n NodePos) Pos() int {
16	return int(n)
17}
18
19//NodeType is a safer way to determine a node's type than type assertions.
20type NodeType int
21
22//GetNodeType returns the node's type.
23func (t NodeType) GetNodeType() NodeType {
24	return t
25}
26
27//These are all the possible node types
28const (
29	NtAttr NodeType = iota
30	NtChd
31	NtComm
32	NtElem
33	NtNs
34	NtRoot
35	NtPi
36)
37
38//Node is a XPath result that is a node except elements
39type Node interface {
40	//ResValue prints the node's string value
41	ResValue() string
42	//Pos returns the node's position in the document order
43	Pos() int
44	//GetToken returns the xml.Token representation of the node
45	GetToken() xml.Token
46	//GetParent returns the parent node, which will always be an XML element
47	GetParent() Elem
48	//GetNodeType returns the node's type
49	GetNodeType() NodeType
50}
51
52//Elem is a XPath result that is an element node
53type Elem interface {
54	Node
55	//GetChildren returns the elements children.
56	GetChildren() []Node
57	//GetAttrs returns the attributes of the element
58	GetAttrs() []Node
59}
60
61//NSElem is a node that keeps track of namespaces.
62type NSElem interface {
63	Elem
64	GetNS() map[xml.Name]string
65}
66
67//NSBuilder is a helper-struct for satisfying the NSElem interface
68type NSBuilder struct {
69	NS map[xml.Name]string
70}
71
72//GetNS returns the namespaces found on the current element.  It should not be
73//confused with BuildNS, which actually resolves the namespace nodes.
74func (ns NSBuilder) GetNS() map[xml.Name]string {
75	return ns.NS
76}
77
78type nsValueSort []NS
79
80func (ns nsValueSort) Len() int { return len(ns) }
81func (ns nsValueSort) Swap(i, j int) {
82	ns[i], ns[j] = ns[j], ns[i]
83}
84func (ns nsValueSort) Less(i, j int) bool {
85	return ns[i].Value < ns[j].Value
86}
87
88//BuildNS resolves all the namespace nodes of the element and returns them
89func BuildNS(t Elem) (ret []NS) {
90	vals := make(map[xml.Name]string)
91
92	if nselem, ok := t.(NSElem); ok {
93		buildNS(nselem, vals)
94
95		ret = make([]NS, 0, len(vals))
96		i := 1
97
98		for k, v := range vals {
99			if !(k.Local == "xmlns" && k.Space == "" && v == "") {
100				ret = append(ret, NS{
101					Attr:     xml.Attr{Name: k, Value: v},
102					Parent:   t,
103					NodeType: NtNs,
104				})
105				i++
106			}
107		}
108
109		sort.Sort(nsValueSort(ret))
110		for i := range ret {
111			ret[i].NodePos = NodePos(t.Pos() + i + 1)
112		}
113	}
114
115	return ret
116}
117
118func buildNS(x NSElem, ret map[xml.Name]string) {
119	if x.GetNodeType() == NtRoot {
120		return
121	}
122
123	if nselem, ok := x.GetParent().(NSElem); ok {
124		buildNS(nselem, ret)
125	}
126
127	for k, v := range x.GetNS() {
128		ret[k] = v
129	}
130}
131
132//NS is a namespace node.
133type NS struct {
134	xml.Attr
135	Parent Elem
136	NodePos
137	NodeType
138}
139
140//GetToken returns the xml.Token representation of the namespace.
141func (ns NS) GetToken() xml.Token {
142	return ns.Attr
143}
144
145//GetParent returns the parent node of the namespace.
146func (ns NS) GetParent() Elem {
147	return ns.Parent
148}
149
150//ResValue returns the string value of the namespace
151func (ns NS) ResValue() string {
152	return ns.Attr.Value
153}
154
155//GetAttribute is a convenience function for getting the specified attribute from an element.
156//false is returned if the attribute is not found.
157func GetAttribute(n Elem, local, space string) (xml.Attr, bool) {
158	attrs := n.GetAttrs()
159	for _, i := range attrs {
160		attr := i.GetToken().(xml.Attr)
161		if local == attr.Name.Local && space == attr.Name.Space {
162			return attr, true
163		}
164	}
165	return xml.Attr{}, false
166}
167
168//GetAttributeVal is like GetAttribute, except it returns the attribute's value.
169func GetAttributeVal(n Elem, local, space string) (string, bool) {
170	attr, ok := GetAttribute(n, local, space)
171	return attr.Value, ok
172}
173
174//GetAttrValOrEmpty is like GetAttributeVal, except it returns an empty string if
175//the attribute is not found instead of false.
176func GetAttrValOrEmpty(n Elem, local, space string) string {
177	val, ok := GetAttributeVal(n, local, space)
178	if !ok {
179		return ""
180	}
181	return val
182}
183
184//FindNodeByPos finds a node from the given position.  Returns nil if the node
185//is not found.
186func FindNodeByPos(n Node, pos int) Node {
187	if n.Pos() == pos {
188		return n
189	}
190
191	if elem, ok := n.(Elem); ok {
192		chldrn := elem.GetChildren()
193		for i := 1; i < len(chldrn); i++ {
194			if chldrn[i-1].Pos() <= pos && chldrn[i].Pos() > pos {
195				return FindNodeByPos(chldrn[i-1], pos)
196			}
197		}
198
199		if len(chldrn) > 0 {
200			if chldrn[len(chldrn)-1].Pos() <= pos {
201				return FindNodeByPos(chldrn[len(chldrn)-1], pos)
202			}
203		}
204
205		attrs := elem.GetAttrs()
206		for _, i := range attrs {
207			if i.Pos() == pos {
208				return i
209			}
210		}
211
212		ns := BuildNS(elem)
213		for _, i := range ns {
214			if i.Pos() == pos {
215				return i
216			}
217		}
218	}
219
220	return nil
221}
222