1 /*
2 * @(#)NodeImpl.java 1.36 02/03/21
3 *
4 * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
5 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6 */
7 package org.enhydra.xml;
8
9 import java.io.File;
10 import java.io.FileOutputStream;
11 import java.util.ArrayList;
12 import java.util.Iterator;
13 import java.util.List;
14 import java.util.HashMap;
15 import java.util.HashMap;
16
17 import org.w3c.dom.Attr;
18 import org.w3c.dom.Document;
19 import org.w3c.dom.Element;
20 import org.w3c.dom.DOMException;
21 import org.w3c.dom.NamedNodeMap;
22 import org.w3c.dom.Node;
23 import org.w3c.dom.NodeList;
24
25 /***
26 * @author Tweety
27 *
28 * A class representing a node in a meta-data tree, which implements
29 * the <a href="../../../../api/org/w3c/dom/Node.html">
30 *
31 * <p> Namespaces are ignored in this implementation. The terms "tag
32 * name" and "node name" are always considered to be synonymous.
33 *
34 * @version 1.0
35 */
36 public class NodeImpl implements Node, NodeList {
37
38 /***
39 * Owner document.
40 */
41 protected Document ownerDocument;
42
43
44 /***
45 * The name (tag) of the node as a <code>String</code>.
46 */
47 protected String nodeName = null;
48
49
50 /***
51 * The value of the node as a <code>String</code>.
52 */
53 protected String nodeValue = null;
54
55
56 /***
57 * The type of the node as a <code>short</code>.
58 */
59 protected short type = ELEMENT_NODE;
60
61
62 /***
63 * The parent node of this node, or <code>null</code> if this node
64 * forms the root of its own tree.
65 */
66 protected NodeImpl parent = null;
67
68
69 /***
70 * The number of child nodes.
71 */
72 protected int numChildren = 0;
73
74
75 /***
76 * The first (leftmost) child node of this node, or
77 * <code>null</code> if this node is a leaf node.
78 */
79 protected NodeImpl firstChild = null;
80
81
82 /***
83 * The last (rightmost) child node of this node, or
84 * <code>null</code> if this node is a leaf node.
85 */
86 protected NodeImpl lastChild = null;
87
88
89 /***
90 * The next (right) sibling node of this node, or
91 * <code>null</code> if this node is its parent's last child node.
92 */
93 protected NodeImpl nextSibling = null;
94
95
96 /***
97 * The previous (left) sibling node of this node, or
98 * <code>null</code> if this node is its parent's first child node.
99 */
100 protected NodeImpl previousSibling = null;
101
102
103 /***
104 * Constructs an empty <code>NodeImpl</code>.
105 */
106 public NodeImpl() {
107 }
108
109
110 /***
111 * Constructs a <code>NodeImpl</code> from the given node,
112 * without creating entire children subtree.
113 *
114 * @param node, as a <code>NodeImpl</code>.
115 */
116 public NodeImpl(NodeImpl node) {
117 ownerDocument = node.ownerDocument;
118 nodeName = node.nodeName;
119 nodeValue = node.nodeValue;
120 type = node.type;
121 parent = node.parent;
122 numChildren = node.numChildren;
123 firstChild = node.firstChild;
124 lastChild = node.lastChild;
125 nextSibling = node.nextSibling;
126 previousSibling = node.previousSibling;
127 }
128
129
130 /***
131 * Constructs an <code>NodeImpl</code> from a given node (creates the children subtree too),
132 * as a <code>Node</code>
133 *
134 * @param node, as a <code>Node</code>.
135 */
136 public NodeImpl(Node node) {
137 this(node, true);
138 }
139
140
141 /***
142 * Constructs an <code>NodeImpl</code> from a given node, as a <code>Node</code>,
143 * and deep as <code>boolean</code>.
144 *
145 * @param node, as a <code>Node</code>.
146 * @param deep if <code>true</code>, recursively clone the subtree
147 * under the specified node; if <code>false</code>, clone only the
148 * node itself.
149 */
150 public NodeImpl(Node node, boolean deep) {
151 this.ownerDocument = node.getOwnerDocument();
152 this.nodeName = node.getNodeName();
153 this.type = node.getNodeType();
154 this.nodeValue = node.getNodeValue();
155 if (deep)
156 this.initNodeImplChildren(node);
157 }
158
159
160 /***
161 * Constructs a <code>NodeImpl</code> from the given document owner and node name.
162 *
163 * @param ownerDoc the document owner of the node, as a <code>Document</code>.
164 * @param name the name of the node, as a <code>String</code>.
165 */
166 public NodeImpl(Document ownerDoc, String name) {
167 this.ownerDocument = ownerDoc;
168 this.nodeName = nodeName;
169 }
170
171
172 /***
173 * Constructs an <code>NodeImpl</code> from a given document owner,
174 * node name and node type.
175 *
176 * @param ownerDoc the document owner of the node, as a <code>Document</code>.
177 * @param nodeName the name of the node, as a <code>String</code>.
178 * @param type the type of the node, as a <code>short</code>.
179 */
180 public NodeImpl(Document ownerDoc, String nodeName, short type) {
181 this.ownerDocument = ownerDoc;
182 this.nodeName = nodeName;
183 this.type = type;
184 }
185
186
187 /***
188 * Constructs an <code>NodeImpl</code> from a given document owner,
189 * node name, node type and node value.
190 *
191 * @param ownerDoc the document owner of the node, as a <code>Document</code>.
192 * @param nodeName the name of the node, as a <code>String</code>.
193 * @param type the type of the node, as a <code>short</code>.
194 * @param value the value of the node, as a <code>String</code>.
195 */
196 public NodeImpl(Document ownerDoc, String nodeName, short type, String value) {
197 this.ownerDocument = ownerDoc;
198 this.nodeName = nodeName;
199 this.type = type;
200 this.nodeValue = value;
201 }
202
203
204
205 /***
206 * Creates the children subtree and adds to this node.
207 * (this part had to be splited from the constructor)
208 *
209 * @param nodeas a <code>Node</code>.
210 */
211 protected void initNodeImplChildren(Node node) {
212 // add children
213 Node child = node.getFirstChild();
214 while (child != null) {
215 switch (child.getNodeType()) {
216 case Node.ELEMENT_NODE : {
217 this.appendChild(newElementInstance(child));
218 }; break;
219 case Node.TEXT_NODE : {
220 this.appendChild(newTextInstance(child));
221 }; break;
222 case Node.COMMENT_NODE : {
223 this.appendChild(newCommentInstance(child));
224 }; break;
225 default : {
226 this.appendChild(newDefaultInstance(child));
227 };
228 }
229 child = child.getNextSibling();
230 }
231 }
232
233
234 /***
235 * Creates new instance of the ElementImpl class.
236 *
237 * @param node, as a <code>Node</code>.
238 * @return Node new instance of the ElementImpl class.
239 */
240 protected Node newElementInstance(Node node) {
241 return new ElementImpl(node);
242 }
243
244 /***
245 * Creates new instance of the TextImpl class.
246 *
247 * @param node, as a <code>Node</code>.
248 * @return Node new instance of the TextImpl class.
249 */
250 protected Node newTextInstance(Node node) {
251 return new TextImpl(node);
252 }
253
254 /***
255 * Creates new instance of the CommentImpl class.
256 *
257 * @param node, as a <code>Node</code>.
258 * @return Node new instance of the CommentImpl class.
259 */
260 protected Node newCommentInstance(Node node) {
261 return new CommentImpl(node);
262 }
263
264 /***
265 * Creates new instance of the NodeImpl class.
266 *
267 * @param node, as a <code>Node</code>.
268 * @return Node new instance of the NodeImpl class.
269 */
270 protected Node newDefaultInstance(Node node) {
271 return new NodeImpl(node);
272 }
273
274
275 /***
276 * Check that the node is either <code>null</code> or an
277 * <code>NodeImpl</code>.
278 *
279 * @exception DOMException if node is not an instance of <code>NodeImpl</code>.
280 */
281 private void checkNode(Node node) throws DOMException {
282 if (node == null) {
283 return;
284 }
285 if (!(node instanceof NodeImpl))
286 throw new NodeDOMException(DOMException.WRONG_DOCUMENT_ERR, "Node not an NodeImpl!");
287 }
288
289
290
291 // Methods from Node
292
293 /***
294 * Returns the name associated with this node.
295 *
296 * @return the name, as a <code>String</code>.
297 */
298 public String getNodeName() {
299 return nodeName;
300 }
301
302
303 /***
304 * Returns the value associated with this node.
305 *
306 * @return the node value, as a <code>String</code>.
307 */
308 public String getNodeValue() {
309 return nodeValue;
310 }
311
312
313 /***
314 * Sets the node value of this node.
315 *
316 * @param nodeValue new node value, as a <code>String</code>.
317 */
318 public void setNodeValue(String nodeValue) {
319 this.nodeValue = nodeValue;
320 }
321
322
323 /***
324 * Returns the node type.
325 *
326 * @return the <code>short</code> value node type.
327 */
328 public short getNodeType() {
329 return type;
330 }
331
332
333 /***
334 * Returns the parent of this node. A <code>null</code> value
335 * indicates that the node is the root of its own tree. To add a
336 * node to an existing tree, use one of the
337 * <code>insertBefore</code>, <code>replaceChild</code>, or
338 * <code>appendChild</code> methods.
339 *
340 * @return the parent, as a <code>Node</code>.
341 *
342 * @see #insertBefore
343 * @see #replaceChild
344 * @see #appendChild
345 */
346 public Node getParentNode() {
347 return parent;
348 }
349
350
351 /***
352 * Returns all child nodes of this node, or <code>null</code> if
353 * the node has no children.
354 *
355 * @return all child nodes of this node, as a <code>Node</code>, or
356 * <code>null</code>.
357 */
358 public NodeList getChildNodes() {
359 return this;
360 }
361
362
363 /***
364 * Returns the first child of this node, or <code>null</code> if
365 * the node has no children.
366 *
367 * @return the first child, as a <code>Node</code>, or
368 * <code>null</code>
369 */
370 public Node getFirstChild() {
371 return firstChild;
372 }
373
374
375 /***
376 * Returns the last child of this node, or <code>null</code> if
377 * the node has no children.
378 *
379 * @return the last child, as a <code>Node</code>, or
380 * <code>null</code>.
381 */
382 public Node getLastChild() {
383 return lastChild;
384 }
385
386
387 /***
388 * Returns the previous sibling of this node, or <code>null</code>
389 * if this node has no previous sibling.
390 *
391 * @return the previous sibling, as a <code>Node</code>, or
392 * <code>null</code>.
393 */
394 public Node getPreviousSibling() {
395 return previousSibling;
396 }
397
398
399 /***
400 * Returns the next sibling of this node, or <code>null</code> if
401 * the node has no next sibling.
402 *
403 * @return the next sibling, as a <code>Node</code>, or
404 * <code>null</code>.
405 */
406 public Node getNextSibling() {
407 return nextSibling;
408 }
409
410
411 /***
412 * Returns <code>null</code>, since <code>NodeImpl</code>s
413 * do not belong to any <code>Document</code>.
414 *
415 * @return document owner as <code>Document</code>.
416 */
417 public Document getOwnerDocument() {
418 return ownerDocument;
419 }
420
421
422 /***
423 * Inserts the node <code>newChild</code> before the existing
424 * child node <code>refChild</code>. If <code>refChild</code> is
425 * <code>null</code>, insert <code>newChild</code> at the end of
426 * the list of children.
427 *
428 * @param newChild the <code>Node</code> to insert.
429 * @param refChild the reference <code>Node</code>.
430 *
431 * @return the node being inserted.
432 *
433 * @exception IllegalArgumentException if <code>newChild</code> is
434 * <code>null</code>.
435 */
436 public Node insertBefore(Node newChild, Node refChild) {
437 if (newChild == null) {
438 throw new IllegalArgumentException("newChild == null!");
439 }
440
441 checkNode(newChild);
442 checkNode(refChild);
443
444 NodeImpl newChildNode = (NodeImpl) newChild;
445 NodeImpl refChildNode = (NodeImpl) refChild;
446
447 // Siblings, can be null.
448 NodeImpl previous = null;
449 NodeImpl next = null;
450
451 if (refChild == null) {
452 previous = this.lastChild;
453 next = null;
454 this.lastChild = newChildNode;
455 } else {
456 previous = refChildNode.previousSibling;
457 next = refChildNode;
458 }
459
460 if (previous != null) {
461 previous.nextSibling = newChildNode;
462 }
463 if (next != null) {
464 next.previousSibling = newChildNode;
465 }
466
467 newChildNode.parent = this;
468 newChildNode.previousSibling = previous;
469 newChildNode.nextSibling = next;
470
471 // N.B.: O.K. if refChild == null
472 if (this.firstChild == refChildNode) {
473 this.firstChild = newChildNode;
474 }
475 ++numChildren;
476 return newChildNode;
477 }
478
479
480 /***
481 * Replaces the child node <code>oldChild</code> with
482 * <code>newChild</code> in the list of children, and returns the
483 * <code>oldChild</code> node.
484 *
485 * @param newChild the <code>Node</code> to insert.
486 * @param oldChild the <code>Node</code> to be replaced.
487 *
488 * @return the node replaced.
489 *
490 * @exception IllegalArgumentException if <code>newChild</code> is
491 * <code>null</code>.
492 */
493 public Node replaceChild(Node newChild, Node oldChild) {
494 if (newChild == null) {
495 throw new IllegalArgumentException("newChild == null!");
496 }
497
498 checkNode(newChild);
499 checkNode(oldChild);
500
501 NodeImpl newChildNode = (NodeImpl) newChild;
502 NodeImpl oldChildNode = (NodeImpl) oldChild;
503
504 NodeImpl previous = oldChildNode.previousSibling;
505 NodeImpl next = oldChildNode.nextSibling;
506
507 if (previous != null) {
508 previous.nextSibling = newChildNode;
509 }
510 if (next != null) {
511 next.previousSibling = newChildNode;
512 }
513
514 newChildNode.parent = this;
515 newChildNode.previousSibling = previous;
516 newChildNode.nextSibling = next;
517
518 if (firstChild == oldChildNode) {
519 firstChild = newChildNode;
520 }
521 if (lastChild == oldChildNode) {
522 lastChild = newChildNode;
523 }
524
525 oldChildNode.parent = null;
526 oldChildNode.previousSibling = null;
527 oldChildNode.nextSibling = null;
528
529 return oldChildNode;
530 }
531
532
533 /***
534 * Removes the child node indicated by <code>oldChild</code> from
535 * the list of children, and returns it.
536 *
537 * @param oldChild the <code>Node</code> to be removed.
538 *
539 * @return the node removed.
540 *
541 * @exception IllegalArgumentException if <code>oldChild</code> is
542 * <code>null</code>.
543 */
544 public Node removeChild(Node oldChild) {
545 if (oldChild == null) {
546 throw new IllegalArgumentException("oldChild == null!");
547 }
548 checkNode(oldChild);
549
550 NodeImpl oldChildNode = (NodeImpl) oldChild;
551
552 NodeImpl previous = oldChildNode.previousSibling;
553 NodeImpl next = oldChildNode.nextSibling;
554
555 if (previous != null) {
556 previous.nextSibling = next;
557 }
558 if (next != null) {
559 next.previousSibling = previous;
560 }
561
562 if (this.firstChild == oldChildNode) {
563 this.firstChild = next;
564 }
565 if (this.lastChild == oldChildNode) {
566 this.lastChild = previous;
567 }
568
569 oldChildNode.parent = null;
570 oldChildNode.previousSibling = null;
571 oldChildNode.nextSibling = null;
572
573 --numChildren;
574 return oldChildNode;
575 }
576
577
578 /***
579 * Adds the node <code>newChild</code> to the end of the list of
580 * children of this node.
581 *
582 * @param newChild the <code>Node</code> to insert.
583 *
584 * @return the node added.
585 *
586 * @exception IllegalArgumentException if <code>newChild</code> is
587 * <code>null</code>.
588 */
589 public Node appendChild(Node newChild) {
590 if (newChild == null) {
591 throw new IllegalArgumentException("newChild == null!");
592 }
593 checkNode(newChild);
594
595 // insertBefore will increment numChildren
596 return insertBefore(newChild, null);
597 }
598
599
600 /***
601 * Returns <code>true</code> if this node has child nodes.
602 *
603 * @return <code>true</code> if this node has children.
604 */
605 public boolean hasChildNodes() {
606 return numChildren > 0;
607 }
608
609
610 /***
611 * Returns a duplicate of this node. The duplicate node has no
612 * parent (<code>getParentNode</code> returns <code>null</code>).
613 * If a shallow clone is being performed (<code>deep</code> is
614 * <code>false</code>), the new node will not have any children or
615 * siblings. If a deep clone is being performed, the new node
616 * will form the root of a complete cloned subtree.
617 *
618 * @param deep if <code>true</code>, recursively clone the subtree
619 * under the specified node; if <code>false</code>, clone only the
620 * node itself.
621 *
622 * @return the duplicate node.
623 */
624 public Node cloneNode(boolean deep) {
625 return new NodeImpl(this, deep);
626 }
627
628
629 /***
630 * Does nothing, since <code>NodeImpl</code>s do not
631 * contain <code>Text</code> children.
632 */
633 public void normalize() {
634 }
635
636
637 /***
638 * Returns <code>false</code> since DOM features are not
639 * supported.
640 *
641 * @return <code>false</code>.
642 *
643 * @param feature a <code>String</code>, which is ignored.
644 * @param version a <code>String</code>, which is ignored.
645 */
646 public boolean isSupported(String feature, String version) {
647 return false;
648 }
649
650
651 /***
652 * Returns <code>null</code>, since namespaces are not supported.
653 */
654 public String getNamespaceURI() throws DOMException {
655 return null;
656 }
657
658
659 /***
660 * Returns <code>null</code>, since namespaces are not supported.
661 *
662 * @return <code>null</code>.
663 *
664 * @see #setPrefix
665 */
666 public String getPrefix() {
667 return null;
668 }
669
670
671 /***
672 * Does nothing, since namespaces are not supported.
673 *
674 * @param prefix a <code>String</code>, which is ignored.
675 *
676 * @see #getPrefix
677 */
678 public void setPrefix(String prefix) {
679 }
680
681
682 /***
683 * Equivalent to <code>getNodeName</code>.
684 *
685 * @return the node name, as a <code>String</code>.
686 */
687 public String getLocalName() {
688 return nodeName;
689 }
690
691
692 /***
693 * Returns all attribute nodes of this node.
694 *
695 * @return all attribute nodes of this node.
696 */
697 public NamedNodeMap getAttributes() {
698 return null;
699 }
700
701
702 /***
703 * Returns <code>true</code>, if this node has attributes, otherwise
704 * <code>false</code>.
705 *
706 * @return <code>true</code> if node has attributes, otherwise <code>false</code>..
707 */
708 public boolean hasAttributes() {
709 return false;
710 }
711
712
713
714
715 // Methods from NodeList
716
717
718 /***
719 * Returns number of child nodes.
720 *
721 * @return all number of child nodes.
722 */
723 public int getLength() {
724 return numChildren;
725 }
726
727
728 /***
729 * Returns child node with the given index.
730 *
731 * @return child node with the given index.
732 */
733 public Node item(int index) {
734 if (index < 0) {
735 return null;
736 }
737
738 Node child = getFirstChild();
739 while (child != null && index-- > 0) {
740 child = child.getNextSibling();
741 }
742 return child;
743 }
744
745
746
747 // String methodes
748
749 /***
750 * Returns <code>String</code> representation of this node.
751 *
752 * @return <code>String</code> representation of this node.
753 */
754 public String toString() {
755 return toString(Indent.DEFAULT_TAB);
756 }
757
758
759 /***
760 * Returns <code>String</code> representation of this node.
761 *
762 * @param tab tab for node indentation.
763 *
764 * @return <code>String</code> representation of this node.
765 */
766 public String toString(String tab) {
767 StringBuffer sb = new StringBuffer();
768 this.allToString(sb, new Indent(0,tab));
769 return sb.toString();
770 }
771
772
773 /***
774 * Method beginToString should be redefined in extended classes.
775 * Each type of node has its own <code>beginToString and
776 * <code>endToString</code>. This was added to support
777 * writing of the xml file. The <code>Element</code>
778 * type of node: it writes the beginning tag, then calls
779 * the child's <code>toString</code>, and then writes the ending tag.
780 *
781 * @param sb string buffer to add resulting string.
782 * @param indent used in formating the output.
783 */
784 protected void beginToString(StringBuffer sb, Indent indent) {
785 }
786
787
788 /***
789 * Method endToString should be redefined in extended classes.
790 * Each type of node has its own <code>beginToString and
791 * <code>endToString</code>. This was added to support
792 * writing of the xml file. The <code>Element</code>
793 * type of node: it writes the beginning tag, then calls
794 * the child's <code>toString</code>, and then writes the ending tag.
795 *
796 * @param sb string buffer to add resulting string.
797 * @param indent used in formating the output.
798 */
799 protected void endToString(StringBuffer sb, Indent indent) {
800 }
801
802
803 private void allToString(StringBuffer sb, Indent indent) {
804 this.beginToString(sb, indent);
805 Node child = getFirstChild();
806 while (child != null) {
807 ((NodeImpl) child).allToString(sb, indent);
808 child = child.getNextSibling();
809 }
810 this.endToString(sb, indent);
811 }
812
813 }
814
815
816
This page automatically generated by Maven