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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
|
package tree
import (
"encoding/xml"
"sort"
)
//XMLSpace is the W3C XML namespace
const XMLSpace = "http://www.w3.org/XML/1998/namespace"
//NodePos is a helper for representing the node's document order
type NodePos int
//Pos returns the node's document order position
func (n NodePos) Pos() int {
return int(n)
}
//NodeType is a safer way to determine a node's type than type assertions.
type NodeType int
//GetNodeType returns the node's type.
func (t NodeType) GetNodeType() NodeType {
return t
}
//These are all the possible node types
const (
NtAttr NodeType = iota
NtChd
NtComm
NtElem
NtNs
NtRoot
NtPi
)
//Node is a XPath result that is a node except elements
type Node interface {
//ResValue prints the node's string value
ResValue() string
//Pos returns the node's position in the document order
Pos() int
//GetToken returns the xml.Token representation of the node
GetToken() xml.Token
//GetParent returns the parent node, which will always be an XML element
GetParent() Elem
//GetNodeType returns the node's type
GetNodeType() NodeType
}
//Elem is a XPath result that is an element node
type Elem interface {
Node
//GetChildren returns the elements children.
GetChildren() []Node
//GetAttrs returns the attributes of the element
GetAttrs() []Node
}
//NSElem is a node that keeps track of namespaces.
type NSElem interface {
Elem
GetNS() map[xml.Name]string
}
//NSBuilder is a helper-struct for satisfying the NSElem interface
type NSBuilder struct {
NS map[xml.Name]string
}
//GetNS returns the namespaces found on the current element. It should not be
//confused with BuildNS, which actually resolves the namespace nodes.
func (ns NSBuilder) GetNS() map[xml.Name]string {
return ns.NS
}
type nsValueSort []NS
func (ns nsValueSort) Len() int { return len(ns) }
func (ns nsValueSort) Swap(i, j int) {
ns[i], ns[j] = ns[j], ns[i]
}
func (ns nsValueSort) Less(i, j int) bool {
return ns[i].Value < ns[j].Value
}
//BuildNS resolves all the namespace nodes of the element and returns them
func BuildNS(t Elem) (ret []NS) {
vals := make(map[xml.Name]string)
if nselem, ok := t.(NSElem); ok {
buildNS(nselem, vals)
ret = make([]NS, 0, len(vals))
i := 1
for k, v := range vals {
if !(k.Local == "xmlns" && k.Space == "" && v == "") {
ret = append(ret, NS{
Attr: xml.Attr{Name: k, Value: v},
Parent: t,
NodeType: NtNs,
})
i++
}
}
sort.Sort(nsValueSort(ret))
for i := range ret {
ret[i].NodePos = NodePos(t.Pos() + i + 1)
}
}
return ret
}
func buildNS(x NSElem, ret map[xml.Name]string) {
if x.GetNodeType() == NtRoot {
return
}
if nselem, ok := x.GetParent().(NSElem); ok {
buildNS(nselem, ret)
}
for k, v := range x.GetNS() {
ret[k] = v
}
}
//NS is a namespace node.
type NS struct {
xml.Attr
Parent Elem
NodePos
NodeType
}
//GetToken returns the xml.Token representation of the namespace.
func (ns NS) GetToken() xml.Token {
return ns.Attr
}
//GetParent returns the parent node of the namespace.
func (ns NS) GetParent() Elem {
return ns.Parent
}
//ResValue returns the string value of the namespace
func (ns NS) ResValue() string {
return ns.Attr.Value
}
//GetAttribute is a convenience function for getting the specified attribute from an element.
//false is returned if the attribute is not found.
func GetAttribute(n Elem, local, space string) (xml.Attr, bool) {
attrs := n.GetAttrs()
for _, i := range attrs {
attr := i.GetToken().(xml.Attr)
if local == attr.Name.Local && space == attr.Name.Space {
return attr, true
}
}
return xml.Attr{}, false
}
//GetAttributeVal is like GetAttribute, except it returns the attribute's value.
func GetAttributeVal(n Elem, local, space string) (string, bool) {
attr, ok := GetAttribute(n, local, space)
return attr.Value, ok
}
//GetAttrValOrEmpty is like GetAttributeVal, except it returns an empty string if
//the attribute is not found instead of false.
func GetAttrValOrEmpty(n Elem, local, space string) string {
val, ok := GetAttributeVal(n, local, space)
if !ok {
return ""
}
return val
}
//FindNodeByPos finds a node from the given position. Returns nil if the node
//is not found.
func FindNodeByPos(n Node, pos int) Node {
if n.Pos() == pos {
return n
}
if elem, ok := n.(Elem); ok {
chldrn := elem.GetChildren()
for i := 1; i < len(chldrn); i++ {
if chldrn[i-1].Pos() <= pos && chldrn[i].Pos() > pos {
return FindNodeByPos(chldrn[i-1], pos)
}
}
if len(chldrn) > 0 {
if chldrn[len(chldrn)-1].Pos() <= pos {
return FindNodeByPos(chldrn[len(chldrn)-1], pos)
}
}
attrs := elem.GetAttrs()
for _, i := range attrs {
if i.Pos() == pos {
return i
}
}
ns := BuildNS(elem)
for _, i := range ns {
if i.Pos() == pos {
return i
}
}
}
return nil
}
|