1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package groovy.util.slurpersupport;
19
20 import groovy.lang.Buildable;
21 import groovy.lang.Closure;
22 import groovy.lang.GroovyObject;
23 import groovy.lang.Writable;
24
25 import java.io.IOException;
26 import java.io.Writer;
27 import java.util.HashMap;
28 import java.util.Iterator;
29 import java.util.LinkedList;
30 import java.util.List;
31 import java.util.Map;
32
33
34 /***
35 * @author John Wilson
36 *
37 */
38
39 public class Node implements Writable {
40 private final String name;
41 private final Map attributes;
42 private final Map attributeNamespaces;
43 private final String namespaceURI;
44 private List children = new LinkedList();
45
46 public Node(final Node parent, final String name, final Map attributes, final Map attributeNamespaces, final String namespaceURI) {
47 this.name = name;
48 this.attributes = attributes;
49 this.attributeNamespaces = attributeNamespaces;
50 this.namespaceURI = namespaceURI;
51 }
52
53 public String name() {
54 return this.name;
55 }
56
57 public String namespaceURI() {
58 return this.namespaceURI;
59 }
60
61 public Map attributes() {
62 return this.attributes;
63 }
64
65 public List children() {
66 return this.children();
67 }
68
69 public void addChild(final Object child) {
70 this.children.add(child);
71 }
72
73
74
75
76 public String text() {
77 final StringBuffer buff = new StringBuffer();
78 final Iterator iter = this.children.iterator();
79
80 while (iter.hasNext()) {
81 buff.append(iter.next());
82 }
83
84 return buff.toString();
85 }
86
87
88
89
90
91 public Iterator childNodes() {
92 return new Iterator() {
93 private final Iterator iter = Node.this.children.iterator();
94 private Object nextElementNodes = getNextElementNodes();
95
96 public boolean hasNext() {
97 return this.nextElementNodes != null;
98 }
99
100 public Object next() {
101 try {
102 return this.nextElementNodes;
103 } finally {
104 this.nextElementNodes = getNextElementNodes();
105 }
106 }
107
108 public void remove() {
109 throw new UnsupportedOperationException();
110 }
111
112 private Object getNextElementNodes() {
113 while (iter.hasNext()) {
114 final Object node = iter.next();
115
116 if (node instanceof Node) {
117 return node;
118 }
119 }
120
121 return null;
122 }
123 };
124 }
125
126
127
128
129 public Writer writeTo(final Writer out) throws IOException {
130 final Iterator iter = this.children.iterator();
131
132 while (iter.hasNext()) {
133 final Object child = iter.next();
134
135 if (child instanceof Writable) {
136 ((Writable)child).writeTo(out);
137 } else {
138 out.write(child.toString());
139 }
140 }
141
142 return out;
143 }
144
145 public void build(final GroovyObject builder, final Map namespaceMap, final Map namespaceTagHints) {
146 final Closure rest = new Closure(null) {
147 public Object doCall(final Object o) {
148 buildChildren(builder, namespaceMap, namespaceTagHints);
149
150 return null;
151 }
152 };
153
154 if (this.namespaceURI.length() == 0 && this.attributeNamespaces.isEmpty()) {
155 builder.invokeMethod(this.name, new Object[]{this.attributes, rest});
156 } else {
157 final List newTags = new LinkedList();
158 builder.getProperty("mkp");
159 final List namespaces = (List)builder.invokeMethod("getNamespaces", new Object[]{});
160
161 final Map current = (Map)namespaces.get(0);
162 final Map pending = (Map)namespaces.get(1);
163
164 if (this.attributeNamespaces.isEmpty()) {
165 builder.getProperty(getTagFor(this.namespaceURI, current, pending, namespaceMap, namespaceTagHints, newTags, builder));
166 builder.invokeMethod(this.name, new Object[]{this.attributes, rest});
167 } else {
168 final Map attributesWithNamespaces = new HashMap(this.attributes);
169 final Iterator attrs = this.attributes.keySet().iterator();
170
171 while (attrs.hasNext()) {
172 final Object key = attrs.next();
173 final Object attributeNamespaceURI = this.attributeNamespaces.get(key);
174
175 if (attributeNamespaceURI != null) {
176 attributesWithNamespaces.put(getTagFor(attributeNamespaceURI, current, pending, namespaceMap, namespaceTagHints, newTags, builder) +
177 "$" + key, attributesWithNamespaces.remove(key));
178 }
179 }
180
181 builder.getProperty(getTagFor(this.namespaceURI, current, pending, namespaceMap,namespaceTagHints, newTags, builder));
182 builder.invokeMethod(this.name, new Object[]{attributesWithNamespaces, rest});
183 }
184
185
186 if (!newTags.isEmpty()) {
187 final Iterator iter = newTags.iterator();
188
189 do {
190 pending.remove(iter.next());
191 } while (iter.hasNext());
192 }
193
194 }
195 }
196
197 private static String getTagFor(final Object namespaceURI, final Map current,
198 final Map pending, final Map local, final Map tagHints,
199 final List newTags, final GroovyObject builder) {
200 String tag = findNamespaceTag(pending, namespaceURI);
201
202 if (tag == null) {
203 tag = findNamespaceTag(current, namespaceURI);
204
205 if (tag == null) {
206
207 tag = findNamespaceTag(local, namespaceURI);
208
209 if (tag == null || tag.length() == 0) {
210 tag = findNamespaceTag(tagHints, namespaceURI);
211 }
212
213 if (tag == null || tag.length() == 0) {
214 int suffix = 0;
215
216 do {
217 final String posibleTag = "tag" + suffix++;
218
219 if (!pending.containsKey(posibleTag) && !current.containsKey(posibleTag) && !local.containsKey(posibleTag)) {
220 tag = posibleTag;
221 }
222 } while (tag == null);
223 }
224
225 final Map newNamespace = new HashMap();
226 newNamespace.put(tag, namespaceURI);
227 builder.getProperty("mkp");
228 builder.invokeMethod("declareNamespace", new Object[]{newNamespace});
229 newTags.add(tag);
230 }
231 }
232
233 return tag;
234 }
235
236 private static String findNamespaceTag(final Map tagMap, final Object namespaceURI) {
237 if (tagMap.containsValue(namespaceURI)) {
238 final Iterator entries = tagMap.entrySet().iterator();
239
240 while (entries.hasNext()) {
241 final Map.Entry entry = (Map.Entry)entries.next();
242
243 if (namespaceURI.equals(entry.getValue())) {
244 return (String)entry.getKey();
245 }
246 }
247 }
248
249 return null;
250 }
251
252 private void buildChildren(final GroovyObject builder, final Map namespaceMap, final Map namespaceTagHints) {
253 final Iterator iter = this.children.iterator();
254
255 while (iter.hasNext()) {
256 final Object child = iter.next();
257
258 if (child instanceof Node) {
259 ((Node)child).build(builder, namespaceMap, namespaceTagHints);
260 } else if (child instanceof Buildable) {
261 ((Buildable)child).build(builder);
262 } else {
263 builder.getProperty("mkp");
264 builder.invokeMethod("yield", new Object[]{child});
265 }
266 }
267 }
268 }