/
dom.go
174 lines (152 loc) · 3.97 KB
/
dom.go
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
package dom
/*
* Implements a very small, very non-compliant subset of the DOM Core Level 3
* http://www.w3.org/TR/DOM-Level-3-Core/
*
* Copyright (c) 2009, Rob Russell
* Copyright (c) 2010, Jeff Schiller
*/
// FIXME: we use the empty string "" to denote a 'null' value when the data type
// according to the DOM API is expected to be a string. Perhaps return a pointer to a string?
import (
"strings"
"xml"
"os"
"io"
)
const (
DEBUG = true
)
// ====================================
// these are the package-level functions that are the real workhorses
// they only use interface types
func appendChild(p Node, c Node) Node {
// if the child is already in the tree somewhere,
// remove it before reparenting
if c.ParentNode() != nil {
removeChild(c.ParentNode(), c)
}
i := p.ChildNodes().Length()
p.insertChildAt(c, i)
c.setParent(p)
return c
}
func removeChild(p Node, c Node) Node {
p.removeChild(c)
c.setParent(nil)
return c
}
/*
func prevSibling(n Node) Node {
children := n.ParentNode().ChildNodes()
//fmt.Println(n)
for i := children.Length()-1; i > 0; i-- {
//fmt.Println(" ", i, " ", children.Item(i))
if children.Item(i) == n {
return children.Item(i-1)
}
}
return Node(nil)
}
*/
func getElementById(e Element, id string) Element {
if e.NodeType() == ELEMENT_NODE {
// check for an id
if av := e.GetAttribute("id"); av != "" {
if av==id {
return e
}
}
// if not found, check the children
cnodes := e.ChildNodes()
var ix uint
clen := cnodes.Length()
for ix = 0 ; ix < clen ; ix++ {
//for c := range e.c {
// return the first one found
//ce := cnodes.Item(ix).(Element).GetElementById(id)
cnode := cnodes.Item(ix)
// can't cast safely unless it's an Element for reals
if cnode.NodeType() == 1 {
ce := getElementById(cnode.(Element),id)
if ce != nil {
return ce
}
}
}
}
return nil
}
func ParseString(s string) (doc Document, err os.Error) {
doc, err = Parse( strings.NewReader(s) )
return
}
func Parse(r io.Reader) (doc Document, err os.Error) {
// Create parser and get first token
p := xml.NewParser(r)
t, err := p.Token()
if err != nil {
return nil, err
}
d := newDoc()
e := (Node)(nil) // e is the current parent
for t != nil {
switch token := t.(type) {
case xml.StartElement:
el := newElem(token)
for ar := range(token.Attr) {
el.SetAttribute(token.Attr[ar].Name.Local, token.Attr[ar].Value)
}
if e == nil {
// set doc root
// this element is a child of e, the last element we found
e = d.setRoot(el)
} else {
// this element is a child of e, the last element we found
e = e.AppendChild(el)
}
case xml.CharData:
e.AppendChild(newText(token))
case xml.Comment:
e.AppendChild(newComment(token))
case xml.EndElement:
e = e.ParentNode()
default:
// TODO: add handling for other types (text nodes, etc)
}
// get the next token
t, err = p.Token()
}
// Make sure that reading stopped on EOF
if err != os.EOF {
return nil, err
}
// All is good, return the document
return d, nil
}
// called recursively
func toXml(n Node) string {
s := ""
switch n.NodeType() {
case ELEMENT_NODE: // Element Nodes
s += "<" + n.NodeName()
// iterate over attributes
for i := uint(0); i < n.Attributes().Length(); i++ {
a := n.Attributes().Item(i)
s += " " + a.NodeName() + "=\"" + a.NodeValue() + "\""
}
s += ">"
// iterate over children
for ch := uint(0); ch < n.ChildNodes().Length(); ch++ {
s += toXml(n.ChildNodes().Item(ch))
}
s += "</" + n.NodeName() + ">"
case TEXT_NODE: // Text Nodes
s += n.NodeValue()
break
}
return s
}
func ToXml(doc Document) string {
return toXml(doc.DocumentElement())
}