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 package com.generationjava.io.xml;
33
34 import java.util.Hashtable;
35 import java.util.Enumeration;
36 import java.util.Vector;
37
38 /***
39 * An xml tag. It can be a processing instructon, an empty tag or
40 * a normal tag. Currently, if the tag is inside a namespace then
41 * that is a part of the name. That is, all names of tags are
42 * fully qualified by the namespace.
43 */
44 public class XMLNode {
45
46 private static final Enumeration EMPTY = new NullEnumeration();
47
48 private Hashtable myAttrs;
49 private Hashtable myNodes;
50 private Vector myNodeList;
51 private String name;
52 private String value;
53 private boolean pi;
54 private boolean comment;
55 private boolean doctype;
56
57 /***
58 * Empty Constructor.
59 */
60 public XMLNode() {
61 this("");
62 }
63
64 /***
65 * Create a new node with this name.
66 */
67 public XMLNode(String name) {
68 this.name = name;
69 }
70
71 /***
72 * Add a child node to this node.
73 */
74 public void addNode(XMLNode node) {
75 if(this.myNodes == null) {
76 this.myNodes = new Hashtable();
77 this.myNodeList = new Vector();
78 }
79 this.myNodeList.add(node);
80 Object obj = this.myNodes.get( node.getName() );
81 if(obj == null) {
82 this.myNodes.put( node.getName(), node );
83 } else
84 if(obj instanceof XMLNode) {
85 Vector vec = new Vector();
86 vec.addElement(obj);
87 vec.addElement(node);
88 this.myNodes.put( node.getName(), vec );
89 } else
90 if(obj instanceof Vector) {
91 Vector vec = (Vector)obj;
92 vec.addElement(node);
93 }
94 }
95
96
97
98 /***
99 *
100 */
101 public Enumeration enumerateNode(String name) {
102 if(this.myNodes == null) {
103 return EMPTY;
104 }
105 Object obj = this.myNodes.get( name );
106 if(obj == null) {
107 return EMPTY;
108 } else
109 if(obj instanceof Vector) {
110 return ((Vector)obj).elements();
111 } else {
112 return new SingleEnumeration(obj);
113 }
114 }
115
116 /***
117 * Add an attribute with specified name and value.
118 */
119 public void addAttr(String name, String value) {
120 if(this.myAttrs == null) {
121 this.myAttrs = new Hashtable();
122 }
123 value = unescapeXml(value);
124 this.myAttrs.put( name, value );
125 }
126
127 /***
128 * Get the attribute with the specified name.
129 */
130 public String getAttr(String name) {
131 if(myAttrs == null) {
132 return null;
133 }
134 return (String)this.myAttrs.get(name);
135 }
136
137 /***
138 * Enumerate over all the attributes of this node.
139 * In the order they were added.
140 */
141 public Enumeration enumerateAttr() {
142 if(this.myAttrs == null) {
143 return EMPTY;
144 } else {
145 return this.myAttrs.keys();
146 }
147 }
148
149 /***
150 * Get the node with the specified name.
151 */
152 public XMLNode getNode(String name) {
153 if(this.myNodes == null) {
154 return null;
155 }
156 Object obj = this.myNodes.get(name);
157 if(obj instanceof XMLNode) {
158 return (XMLNode)obj;
159 }
160 return null;
161 }
162
163 /***
164 * Enumerate over all of this node's children nodes.
165 */
166 public Enumeration enumerateNode() {
167 if(this.myNodes == null) {
168 return EMPTY;
169 } else {
170
171 return this.myNodeList.elements();
172 }
173 }
174
175 /***
176 * Get the name of this node. Includes the namespace.
177 */
178 public String getName() {
179 return this.name;
180 }
181
182 /***
183 * Get the namespace of this node.
184 */
185 public String getNamespace() {
186 if(this.name.indexOf(":") != -1) {
187 return this.name.substring(0,this.name.indexOf(":"));
188 } else {
189 return "";
190 }
191 }
192
193 /***
194 * Get the tag name of this node. Doesn't include namespace.
195 */
196 public String getTagName() {
197 if(this.name.indexOf(":") != -1) {
198 return this.name.substring(this.name.indexOf(":")+1);
199 } else {
200 return this.name;
201 }
202 }
203
204 /***
205 * Get the appended toString's of the children of this node.
206 * For a text node, it will print out the plaintext.
207 */
208 public String getValue() {
209 if(isComment()) {
210 return "<!-- " + value + " -->";
211 }
212 if(isDocType()) {
213 return "<!DOCTYPE " + value + ">";
214 }
215 if(this.value != null) {
216 return this.value;
217 }
218 if(isInvisible()) {
219 return "";
220 }
221
222 if(this.myNodeList != null) {
223 StringBuffer buffer = new StringBuffer();
224 Enumeration enum = enumerateNode();
225 while(enum.hasMoreElements()) {
226 buffer.append(enum.nextElement().toString());
227 }
228 return buffer.toString();
229 }
230 return null;
231 }
232
233 /***
234 * Set the plaintext contained in this node.
235 */
236 public void setPlaintext(String str) {
237 this.value = unescapeXml(str);
238 }
239
240 /***
241 * Is this a normal tag?
242 * That is, not plaintext, not comment and not a pi.
243 */
244 public boolean isTag() {
245 return !(this.pi || (this.name == null) || (this.value != null));
246 }
247
248 /***
249 * Is it invisible
250 */
251 public boolean isInvisible() {
252 return this.name == null;
253 }
254
255 /***
256 * Set whether this node is invisible or not.
257 */
258 public void setInvisible(boolean b) {
259 if(b) {
260 this.name = null;
261 }
262 }
263
264 /***
265 * Is it a doctype
266 */
267 public boolean isDocType() {
268 return this.doctype;
269 }
270
271 /***
272 * Set whether this node is a doctype or not.
273 */
274 public void setDocType(boolean b) {
275 this.doctype = b;
276 }
277
278 /***
279 * Is it a comment
280 */
281 public boolean isComment() {
282 return this.comment;
283 }
284
285 /***
286 * Set whether this node is a comment or not.
287 */
288 public void setComment(boolean b) {
289 this.comment = b;
290 }
291
292 /***
293 * Is it a processing instruction
294 */
295 public boolean isPI() {
296 return this.pi;
297 }
298
299 /***
300 * Set whether this node is a processing instruction or not.
301 */
302 public void setPI(boolean b) {
303 this.pi = b;
304 }
305
306
307
308
309 /***
310 * Is this node empty.
311 */
312 public boolean isEmpty() {
313 return (this.myNodes == null);
314 }
315
316 /***
317 * Is this a text node.
318 */
319 public boolean isTextNode() {
320 return ((this.value != null) && !comment && !doctype && !pi);
321 }
322
323
324
325
326 /***
327 * Turn this node into a String. Outputs the node as
328 * XML. So a large amount of output.
329 */
330 public String toString() {
331 if(isComment()) {
332 return "<!-- " + value + " -->";
333 }
334 if(isDocType()) {
335 return "<!DOCTYPE " + value + ">";
336 }
337 if(value != null) {
338 return value;
339 }
340
341 StringBuffer tmp = new StringBuffer();
342
343 if(!isInvisible()) {
344 tmp.append("<");
345 if(isPI()) {
346 tmp.append("?");
347 }
348 tmp.append(name);
349 }
350
351 Enumeration enum = enumerateAttr();
352 while(enum.hasMoreElements()) {
353 tmp.append(" ");
354 String obj = (String)enum.nextElement();
355 tmp.append(obj);
356 tmp.append("=\"");
357 tmp.append(getAttr(obj));
358 tmp.append("\"");
359 }
360 if(isEmpty()) {
361 if(isPI()) {
362 tmp.append("?>");
363 } else {
364 if(!isInvisible()) {
365 tmp.append("/>");
366 }
367 }
368 } else {
369 if(!isInvisible()) {
370 tmp.append(">");
371 }
372
373 tmp.append(bodyToString());
374
375 if(!isInvisible()) {
376 tmp.append("</"+name+">\n");
377 }
378 }
379 return tmp.toString();
380 }
381
382 /***
383 * Get the String version of the body of this tag.
384 */
385 public String bodyToString() {
386 StringBuffer tmp = new StringBuffer();
387 Enumeration enum = enumerateNode();
388 while(enum.hasMoreElements()) {
389 Object obj = enum.nextElement();
390 if(obj instanceof XMLNode) {
391 XMLNode node = (XMLNode)obj;
392 tmp.append(node);
393 } else
394 if(obj instanceof Vector) {
395 Vector nodelist = (Vector)obj;
396 Enumeration nodeEnum = nodelist.elements();
397 while(nodeEnum.hasMoreElements()) {
398 XMLNode node = (XMLNode)nodeEnum.nextElement();
399 tmp.append(node);
400 }
401 }
402 }
403 return tmp.toString();
404 }
405
406 private static String unescapeXml(String str) {
407 str = replace(str,"&","&");
408 str = replace(str,"<","<");
409 str = replace(str,">",">");
410 str = replace(str,""","\"");
411 str = replace(str,"'","'");
412 return str;
413 }
414
415
416 private static String replace(String text, String repl, String with) {
417 int max = -1;
418 if (text == null || repl == null || with == null || repl.length() == 0 || max == 0) {
419 return text;
420 }
421
422 StringBuffer buf = new StringBuffer(text.length());
423 int start = 0, end = 0;
424 while ((end = text.indexOf(repl, start)) != -1) {
425 buf.append(text.substring(start, end)).append(with);
426 start = end + repl.length();
427
428 if (--max == 0) {
429 break;
430 }
431 }
432 buf.append(text.substring(start));
433 return buf.toString();
434 }
435
436 }
437
438 /***
439 * An empty enumeration. Nicer to return than just plain null.
440 */
441 class NullEnumeration implements Enumeration {
442 public Object nextElement() {
443 return null;
444 }
445
446 public boolean hasMoreElements() {
447 return false;
448 }
449 }
450 /***
451 * A single enumeration. Saves time on making a Vector.
452 */
453 class SingleEnumeration implements Enumeration {
454
455 private Object obj;
456
457 public SingleEnumeration(Object obj) {
458 this.obj = obj;
459 }
460
461 public Object nextElement() {
462 Object tmp = this.obj;
463 this.obj = null;
464 return tmp;
465 }
466
467 public boolean hasMoreElements() {
468 return (obj != null);
469 }
470 }