summaryrefslogtreecommitdiff
path: root/vendor/github.com/mitchellh/packer/vendor/github.com/ChrisTrenkamp/goxpath/tree/tree.go
blob: c5d6333d71df81ada59e345eed1293ac8f00ba49 (plain)
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
}