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