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 final Object child = iter.next();
82
83 if (child instanceof Node) {
84 buff.append(((Node)child).text());
85 } else {
86 buff.append(child);
87 }
88 }
89
90 return buff.toString();
91 }
92
93
94
95
96
97 public Iterator childNodes() {
98 return new Iterator() {
99 private final Iterator iter = Node.this.children.iterator();
100 private Object nextElementNodes = getNextElementNodes();
101
102 public boolean hasNext() {
103 return this.nextElementNodes != null;
104 }
105
106 public Object next() {
107 try {
108 return this.nextElementNodes;
109 } finally {
110 this.nextElementNodes = getNextElementNodes();
111 }
112 }
113
114 public void remove() {
115 throw new UnsupportedOperationException();
116 }
117
118 private Object getNextElementNodes() {
119 while (iter.hasNext()) {
120 final Object node = iter.next();
121
122 if (node instanceof Node) {
123 return node;
124 }
125 }
126
127 return null;
128 }
129 };
130 }
131
132
133
134
135 public Writer writeTo(final Writer out) throws IOException {
136 final Iterator iter = this.children.iterator();
137
138 while (iter.hasNext()) {
139 final Object child = iter.next();
140
141 if (child instanceof Writable) {
142 ((Writable)child).writeTo(out);
143 } else {
144 out.write(child.toString());
145 }
146 }
147
148 return out;
149 }
150
151 public void build(final GroovyObject builder, final Map namespaceMap, final Map namespaceTagHints) {
152 final Closure rest = new Closure(null) {
153 public Object doCall(final Object o) {
154 buildChildren(builder, namespaceMap, namespaceTagHints);
155
156 return null;
157 }
158 };
159
160 if (this.namespaceURI.length() == 0 && this.attributeNamespaces.isEmpty()) {
161 builder.invokeMethod(this.name, new Object[]{this.attributes, rest});
162 } else {
163 final List newTags = new LinkedList();
164 builder.getProperty("mkp");
165 final List namespaces = (List)builder.invokeMethod("getNamespaces", new Object[]{});
166
167 final Map current = (Map)namespaces.get(0);
168 final Map pending = (Map)namespaces.get(1);
169
170 if (this.attributeNamespaces.isEmpty()) {
171 builder.getProperty(getTagFor(this.namespaceURI, current, pending, namespaceMap, namespaceTagHints, newTags, builder));
172 builder.invokeMethod(this.name, new Object[]{this.attributes, rest});
173 } else {
174 final Map attributesWithNamespaces = new HashMap(this.attributes);
175 final Iterator attrs = this.attributes.keySet().iterator();
176
177 while (attrs.hasNext()) {
178 final Object key = attrs.next();
179 final Object attributeNamespaceURI = this.attributeNamespaces.get(key);
180
181 if (attributeNamespaceURI != null) {
182 attributesWithNamespaces.put(getTagFor(attributeNamespaceURI, current, pending, namespaceMap, namespaceTagHints, newTags, builder) +
183 "$" + key, attributesWithNamespaces.remove(key));
184 }
185 }
186
187 builder.getProperty(getTagFor(this.namespaceURI, current, pending, namespaceMap,namespaceTagHints, newTags, builder));
188 builder.invokeMethod(this.name, new Object[]{attributesWithNamespaces, rest});
189 }
190
191
192 if (!newTags.isEmpty()) {
193 final Iterator iter = newTags.iterator();
194
195 do {
196 pending.remove(iter.next());
197 } while (iter.hasNext());
198 }
199
200 }
201 }
202
203 private static String getTagFor(final Object namespaceURI, final Map current,
204 final Map pending, final Map local, final Map tagHints,
205 final List newTags, final GroovyObject builder) {
206 String tag = findNamespaceTag(pending, namespaceURI);
207
208 if (tag == null) {
209 tag = findNamespaceTag(current, namespaceURI);
210
211 if (tag == null) {
212
213 tag = findNamespaceTag(local, namespaceURI);
214
215 if (tag == null || tag.length() == 0) {
216 tag = findNamespaceTag(tagHints, namespaceURI);
217 }
218
219 if (tag == null || tag.length() == 0) {
220 int suffix = 0;
221
222 do {
223 final String posibleTag = "tag" + suffix++;
224
225 if (!pending.containsKey(posibleTag) && !current.containsKey(posibleTag) && !local.containsKey(posibleTag)) {
226 tag = posibleTag;
227 }
228 } while (tag == null);
229 }
230
231 final Map newNamespace = new HashMap();
232 newNamespace.put(tag, namespaceURI);
233 builder.getProperty("mkp");
234 builder.invokeMethod("declareNamespace", new Object[]{newNamespace});
235 newTags.add(tag);
236 }
237 }
238
239 return tag;
240 }
241
242 private static String findNamespaceTag(final Map tagMap, final Object namespaceURI) {
243 if (tagMap.containsValue(namespaceURI)) {
244 final Iterator entries = tagMap.entrySet().iterator();
245
246 while (entries.hasNext()) {
247 final Map.Entry entry = (Map.Entry)entries.next();
248
249 if (namespaceURI.equals(entry.getValue())) {
250 return (String)entry.getKey();
251 }
252 }
253 }
254
255 return null;
256 }
257
258 private void buildChildren(final GroovyObject builder, final Map namespaceMap, final Map namespaceTagHints) {
259 final Iterator iter = this.children.iterator();
260
261 while (iter.hasNext()) {
262 final Object child = iter.next();
263
264 if (child instanceof Node) {
265 ((Node)child).build(builder, namespaceMap, namespaceTagHints);
266 } else if (child instanceof Buildable) {
267 ((Buildable)child).build(builder);
268 } else {
269 builder.getProperty("mkp");
270 builder.invokeMethod("yield", new Object[]{child});
271 }
272 }
273 }
274 }