001: /*
002: Copyright (C) 2003 Together
003:
004: This library is free software; you can redistribute it and/or
005: modify it under the terms of the GNU Lesser General Public
006: License as published by the Free Software Foundation; either
007: version 2.1 of the License, or (at your option) any later version.
008:
009: This library is distributed in the hope that it will be useful,
010: but WITHOUT ANY WARRANTY; without even the implied warranty of
011: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: Lesser General Public License for more details.
013:
014: You should have received a copy of the GNU Lesser General Public
015: License along with this library; if not, write to the Free Software
016: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */
018:
019: package org.enhydra.xml;
020:
021: import java.io.File;
022: import java.io.FileOutputStream;
023: import java.util.ArrayList;
024: import java.util.Iterator;
025: import java.util.List;
026: import java.util.HashMap;
027: import java.util.HashMap;
028:
029: import org.w3c.dom.Attr;
030: import org.w3c.dom.Document;
031: import org.w3c.dom.Element;
032: import org.w3c.dom.DOMException;
033: import org.w3c.dom.NamedNodeMap;
034: import org.w3c.dom.Node;
035: import org.w3c.dom.NodeList;
036: import org.w3c.dom.UserDataHandler;
037:
038: /**
039: * @author Tweety
040: *
041: * A class representing a node in a meta-data tree, which implements
042: * the <a href="../../../../api/org/w3c/dom/Node.html">
043: *
044: * <p> Namespaces are ignored in this implementation. The terms "tag
045: * name" and "node name" are always considered to be synonymous.
046: *
047: * @version 1.0
048: */
049: public class NodeImpl implements Node, NodeList {
050:
051: /**
052: * Owner document.
053: */
054: protected Document ownerDocument;
055:
056: /**
057: * The name (tag) of the node as a <code>String</code>.
058: */
059: protected String nodeName = null;
060:
061: /**
062: * The value of the node as a <code>String</code>.
063: */
064: protected String nodeValue = null;
065:
066: /**
067: * The type of the node as a <code>short</code>.
068: */
069: protected short type = ELEMENT_NODE;
070:
071: /**
072: * The parent node of this node, or <code>null</code> if this node
073: * forms the root of its own tree.
074: */
075: protected NodeImpl parent = null;
076:
077: /**
078: * The number of child nodes.
079: */
080: protected int numChildren = 0;
081:
082: /**
083: * The first (leftmost) child node of this node, or
084: * <code>null</code> if this node is a leaf node.
085: */
086: protected NodeImpl firstChild = null;
087:
088: /**
089: * The last (rightmost) child node of this node, or
090: * <code>null</code> if this node is a leaf node.
091: */
092: protected NodeImpl lastChild = null;
093:
094: /**
095: * The next (right) sibling node of this node, or
096: * <code>null</code> if this node is its parent's last child node.
097: */
098: protected NodeImpl nextSibling = null;
099:
100: /**
101: * The previous (left) sibling node of this node, or
102: * <code>null</code> if this node is its parent's first child node.
103: */
104: protected NodeImpl previousSibling = null;
105:
106: /**
107: * Constructs an empty <code>NodeImpl</code>.
108: */
109: public NodeImpl() {
110: }
111:
112: /**
113: * Constructs a <code>NodeImpl</code> from the given node,
114: * without creating entire children subtree.
115: *
116: * @param node , as a <code>NodeImpl</code>.
117: */
118: public NodeImpl(NodeImpl node) {
119: ownerDocument = node.ownerDocument;
120: nodeName = node.nodeName;
121: nodeValue = node.nodeValue;
122: type = node.type;
123: parent = node.parent;
124: numChildren = node.numChildren;
125: firstChild = node.firstChild;
126: lastChild = node.lastChild;
127: nextSibling = node.nextSibling;
128: previousSibling = node.previousSibling;
129: }
130:
131: /**
132: * Constructs an <code>NodeImpl</code> from a given node (creates the children subtree too),
133: * as a <code>Node</code>
134: *
135: * @param node , as a <code>Node</code>.
136: */
137: public NodeImpl(Node node) {
138: this (node, true);
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: * Constructs a <code>NodeImpl</code> from the given document owner and node name.
161: *
162: * @param ownerDoc the document owner of the node, as a <code>Document</code>.
163: * @param name the name of the node, as a <code>String</code>.
164: */
165: public NodeImpl(Document ownerDoc, String name) {
166: this .ownerDocument = ownerDoc;
167: this .nodeName = nodeName;
168: }
169:
170: /**
171: * Constructs an <code>NodeImpl</code> from a given document owner,
172: * node name and node type.
173: *
174: * @param ownerDoc the document owner of the node, as a <code>Document</code>.
175: * @param nodeName the name of the node, as a <code>String</code>.
176: * @param type the type of the node, as a <code>short</code>.
177: */
178: public NodeImpl(Document ownerDoc, String nodeName, short type) {
179: this .ownerDocument = ownerDoc;
180: this .nodeName = nodeName;
181: this .type = type;
182: }
183:
184: /**
185: * Constructs an <code>NodeImpl</code> from a given document owner,
186: * node name, node type and node value.
187: *
188: * @param ownerDoc the document owner of the node, as a <code>Document</code>.
189: * @param nodeName the name of the node, as a <code>String</code>.
190: * @param type the type of the node, as a <code>short</code>.
191: * @param value the value of the node, as a <code>String</code>.
192: */
193: public NodeImpl(Document ownerDoc, String nodeName, short type,
194: String value) {
195: this .ownerDocument = ownerDoc;
196: this .nodeName = nodeName;
197: this .type = type;
198: this .nodeValue = value;
199: }
200:
201: /**
202: * Creates the children subtree and adds to this node.
203: * (this part had to be splited from the constructor)
204: *
205: * @param node a <code>Node</code>.
206: */
207: protected void initNodeImplChildren(Node node) {
208: // add children
209: Node child = node.getFirstChild();
210: while (child != null) {
211: switch (child.getNodeType()) {
212: case Node.ELEMENT_NODE: {
213: this .appendChild(newElementInstance(child));
214: }
215: ;
216: break;
217: case Node.TEXT_NODE: {
218: this .appendChild(newTextInstance(child));
219: }
220: ;
221: break;
222: case Node.COMMENT_NODE: {
223: this .appendChild(newCommentInstance(child));
224: }
225: ;
226: break;
227: default: {
228: this .appendChild(newDefaultInstance(child));
229: }
230: ;
231: }
232: child = child.getNextSibling();
233: }
234: }
235:
236: /**
237: * Creates new instance of the ElementImpl class.
238: *
239: * @param node , as a <code>Node</code>.
240: * @return Node new instance of the ElementImpl class.
241: */
242: protected Node newElementInstance(Node node) {
243: return new ElementImpl(node);
244: }
245:
246: /**
247: * Creates new instance of the TextImpl class.
248: *
249: * @param node , as a <code>Node</code>.
250: * @return Node new instance of the TextImpl class.
251: */
252: protected Node newTextInstance(Node node) {
253: return new TextImpl(node);
254: }
255:
256: /**
257: * Creates new instance of the CommentImpl class.
258: *
259: * @param node , as a <code>Node</code>.
260: * @return Node new instance of the CommentImpl class.
261: */
262: protected Node newCommentInstance(Node node) {
263: return new CommentImpl(node);
264: }
265:
266: /**
267: * Creates new instance of the NodeImpl class.
268: *
269: * @param node , as a <code>Node</code>.
270: * @return Node new instance of the NodeImpl class.
271: */
272: protected Node newDefaultInstance(Node node) {
273: return new NodeImpl(node);
274: }
275:
276: /**
277: * Check that the node is either <code>null</code> or an
278: * <code>NodeImpl</code>.
279: *
280: * @param node , as a <code>Node</code>.
281: * @exception DOMException if node is not an instance of <code>NodeImpl</code>.
282: */
283: protected void checkNode(Node node) throws DOMException {
284: if (node == null) {
285: return;
286: }
287: if (!(node instanceof NodeImpl))
288: throw new NodeDOMException(DOMException.WRONG_DOCUMENT_ERR,
289: "Node is not an instance of NodeImpl!");
290: }
291:
292: // Methods from Node
293:
294: /**
295: * Returns the name associated with this node.
296: *
297: * @return the name, as a <code>String</code>.
298: */
299: public String getNodeName() {
300: return nodeName;
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: * Sets the node value of this node.
314: *
315: * @param nodeValue new node value, as a <code>String</code>.
316: */
317: public void setNodeValue(String nodeValue) {
318: this .nodeValue = nodeValue;
319: }
320:
321: /**
322: * Returns the node type.
323: *
324: * @return the <code>short</code> value node type.
325: */
326: public short getNodeType() {
327: return type;
328: }
329:
330: /**
331: * Returns the parent of this node. A <code>null</code> value
332: * indicates that the node is the root of its own tree. To add a
333: * node to an existing tree, use one of the
334: * <code>insertBefore</code>, <code>replaceChild</code>, or
335: * <code>appendChild</code> methods.
336: *
337: * @return the parent, as a <code>Node</code>.
338: *
339: * @see #insertBefore
340: * @see #replaceChild
341: * @see #appendChild
342: */
343: public Node getParentNode() {
344: return parent;
345: }
346:
347: /**
348: * Returns all child nodes of this node, or <code>null</code> if
349: * the node has no children.
350: *
351: * @return all child nodes of this node, as a <code>Node</code>, or
352: * <code>null</code>.
353: */
354: public NodeList getChildNodes() {
355: return this ;
356: }
357:
358: /**
359: * Returns the first child of this node, or <code>null</code> if
360: * the node has no children.
361: *
362: * @return the first child, as a <code>Node</code>, or
363: * <code>null</code>
364: */
365: public Node getFirstChild() {
366: return firstChild;
367: }
368:
369: /**
370: * Returns the last child of this node, or <code>null</code> if
371: * the node has no children.
372: *
373: * @return the last child, as a <code>Node</code>, or
374: * <code>null</code>.
375: */
376: public Node getLastChild() {
377: return lastChild;
378: }
379:
380: /**
381: * Returns the previous sibling of this node, or <code>null</code>
382: * if this node has no previous sibling.
383: *
384: * @return the previous sibling, as a <code>Node</code>, or
385: * <code>null</code>.
386: */
387: public Node getPreviousSibling() {
388: return previousSibling;
389: }
390:
391: /**
392: * Returns the next sibling of this node, or <code>null</code> if
393: * the node has no next sibling.
394: *
395: * @return the next sibling, as a <code>Node</code>, or
396: * <code>null</code>.
397: */
398: public Node getNextSibling() {
399: return nextSibling;
400: }
401:
402: /**
403: * Returns <code>null</code>, since <code>NodeImpl</code>s
404: * do not belong to any <code>Document</code>.
405: *
406: * @return document owner as <code>Document</code>.
407: */
408: public Document getOwnerDocument() {
409: return ownerDocument;
410: }
411:
412: /**
413: * Inserts the node <code>newChild</code> before the existing
414: * child node <code>refChild</code>. If <code>refChild</code> is
415: * <code>null</code>, insert <code>newChild</code> at the end of
416: * the list of children.
417: *
418: * @param newChild the <code>Node</code> to insert.
419: * @param refChild the reference <code>Node</code>.
420: *
421: * @return the node being inserted.
422: *
423: * @exception IllegalArgumentException if <code>newChild</code> is
424: * <code>null</code>.
425: */
426: public Node insertBefore(Node newChild, Node refChild) {
427: if (newChild == null) {
428: throw new IllegalArgumentException("newChild == null!");
429: }
430:
431: checkNode(newChild);
432: checkNode(refChild);
433:
434: NodeImpl newChildNode = (NodeImpl) newChild;
435: NodeImpl refChildNode = (NodeImpl) refChild;
436:
437: // Siblings, can be null.
438: NodeImpl previous = null;
439: NodeImpl next = null;
440:
441: if (refChild == null) {
442: previous = this .lastChild;
443: next = null;
444: this .lastChild = newChildNode;
445: } else {
446: previous = refChildNode.previousSibling;
447: next = refChildNode;
448: }
449:
450: if (previous != null) {
451: previous.nextSibling = newChildNode;
452: }
453: if (next != null) {
454: next.previousSibling = newChildNode;
455: }
456:
457: newChildNode.parent = this ;
458: newChildNode.previousSibling = previous;
459: newChildNode.nextSibling = next;
460:
461: // N.B.: O.K. if refChild == null
462: if (this .firstChild == refChildNode) {
463: this .firstChild = newChildNode;
464: }
465: ++numChildren;
466: return newChildNode;
467: }
468:
469: /**
470: * Replaces the child node <code>oldChild</code> with
471: * <code>newChild</code> in the list of children, and returns the
472: * <code>oldChild</code> node.
473: *
474: * @param newChild the <code>Node</code> to insert.
475: * @param oldChild the <code>Node</code> to be replaced.
476: *
477: * @return the node replaced.
478: *
479: * @exception IllegalArgumentException if <code>newChild</code> is
480: * <code>null</code>.
481: */
482: public Node replaceChild(Node newChild, Node oldChild) {
483: if (newChild == null) {
484: throw new IllegalArgumentException("newChild == null!");
485: }
486:
487: checkNode(newChild);
488: checkNode(oldChild);
489:
490: NodeImpl newChildNode = (NodeImpl) newChild;
491: NodeImpl oldChildNode = (NodeImpl) oldChild;
492:
493: NodeImpl previous = oldChildNode.previousSibling;
494: NodeImpl next = oldChildNode.nextSibling;
495:
496: if (previous != null) {
497: previous.nextSibling = newChildNode;
498: }
499: if (next != null) {
500: next.previousSibling = newChildNode;
501: }
502:
503: newChildNode.parent = this ;
504: newChildNode.previousSibling = previous;
505: newChildNode.nextSibling = next;
506:
507: if (firstChild == oldChildNode) {
508: firstChild = newChildNode;
509: }
510: if (lastChild == oldChildNode) {
511: lastChild = newChildNode;
512: }
513:
514: oldChildNode.parent = null;
515: oldChildNode.previousSibling = null;
516: oldChildNode.nextSibling = null;
517:
518: return oldChildNode;
519: }
520:
521: /**
522: * Removes the child node indicated by <code>oldChild</code> from
523: * the list of children, and returns it.
524: *
525: * @param oldChild the <code>Node</code> to be removed.
526: *
527: * @return the node removed.
528: *
529: * @exception IllegalArgumentException if <code>oldChild</code> is
530: * <code>null</code>.
531: */
532: public Node removeChild(Node oldChild) {
533: if (oldChild == null) {
534: throw new IllegalArgumentException("oldChild == null!");
535: }
536: checkNode(oldChild);
537:
538: NodeImpl oldChildNode = (NodeImpl) oldChild;
539:
540: NodeImpl previous = oldChildNode.previousSibling;
541: NodeImpl next = oldChildNode.nextSibling;
542:
543: if (previous != null) {
544: previous.nextSibling = next;
545: }
546: if (next != null) {
547: next.previousSibling = previous;
548: }
549:
550: if (this .firstChild == oldChildNode) {
551: this .firstChild = next;
552: }
553: if (this .lastChild == oldChildNode) {
554: this .lastChild = previous;
555: }
556:
557: oldChildNode.parent = null;
558: oldChildNode.previousSibling = null;
559: oldChildNode.nextSibling = null;
560:
561: --numChildren;
562: return oldChildNode;
563: }
564:
565: /**
566: * Adds the node <code>newChild</code> to the end of the list of
567: * children of this node.
568: *
569: * @param newChild the <code>Node</code> to insert.
570: *
571: * @return the node added.
572: *
573: * @exception IllegalArgumentException if <code>newChild</code> is
574: * <code>null</code>.
575: */
576: public Node appendChild(Node newChild) {
577: if (newChild == null) {
578: throw new IllegalArgumentException("newChild == null!");
579: }
580: checkNode(newChild);
581:
582: // insertBefore will increment numChildren
583: return insertBefore(newChild, null);
584: }
585:
586: /**
587: * Returns <code>true</code> if this node has child nodes.
588: *
589: * @return <code>true</code> if this node has children.
590: */
591: public boolean hasChildNodes() {
592: return numChildren > 0;
593: }
594:
595: /**
596: * Returns a duplicate of this node. The duplicate node has no
597: * parent (<code>getParentNode</code> returns <code>null</code>).
598: * If a shallow clone is being performed (<code>deep</code> is
599: * <code>false</code>), the new node will not have any children or
600: * siblings. If a deep clone is being performed, the new node
601: * will form the root of a complete cloned subtree.
602: *
603: * @param deep if <code>true</code>, recursively clone the subtree
604: * under the specified node; if <code>false</code>, clone only the
605: * node itself.
606: *
607: * @return the duplicate node.
608: */
609: public Node cloneNode(boolean deep) {
610: return new NodeImpl(this , deep);
611: }
612:
613: /**
614: * Does nothing, since <code>NodeImpl</code>s do not
615: * contain <code>Text</code> children.
616: */
617: public void normalize() {
618: }
619:
620: /**
621: * Returns <code>false</code> since DOM features are not
622: * supported.
623: *
624: * @return <code>false</code>.
625: *
626: * @param feature a <code>String</code>, which is ignored.
627: * @param version a <code>String</code>, which is ignored.
628: */
629: public boolean isSupported(String feature, String version) {
630: return false;
631: }
632:
633: /**
634: * @return <code>null</code>, since namespaces are not supported.
635: * @throws DOMException
636: */
637: public String getNamespaceURI() throws DOMException {
638: return null;
639: }
640:
641: /**
642: * Returns <code>null</code>, since namespaces are not supported.
643: *
644: * @return <code>null</code>.
645: *
646: * @see #setPrefix
647: */
648: public String getPrefix() {
649: return null;
650: }
651:
652: /**
653: * Does nothing, since namespaces are not supported.
654: *
655: * @param prefix a <code>String</code>, which is ignored.
656: *
657: * @see #getPrefix
658: */
659: public void setPrefix(String prefix) {
660: }
661:
662: /**
663: * Equivalent to <code>getNodeName</code>.
664: *
665: * @return the node name, as a <code>String</code>.
666: */
667: public String getLocalName() {
668: return nodeName;
669: }
670:
671: /**
672: * Returns all attribute nodes of this node.
673: *
674: * @return all attribute nodes of this node.
675: */
676: public NamedNodeMap getAttributes() {
677: return null;
678: }
679:
680: /**
681: * Returns <code>true</code>, if this node has attributes, otherwise
682: * <code>false</code>.
683: *
684: * @return <code>true</code> if node has attributes, otherwise <code>false</code>..
685: */
686: public boolean hasAttributes() {
687: return false;
688: }
689:
690: // Methods from NodeList
691:
692: /**
693: * Returns number of child nodes.
694: *
695: * @return all number of child nodes.
696: */
697: public int getLength() {
698: return numChildren;
699: }
700:
701: /**
702: * Returns child node with the given index.
703: *
704: * @return child node with the given index.
705: * @param index represents index
706: */
707: public Node item(int index) {
708: if (index < 0) {
709: return null;
710: }
711:
712: Node child = getFirstChild();
713: while (child != null && index-- > 0) {
714: child = child.getNextSibling();
715: }
716: return child;
717: }
718:
719: // String methodes
720:
721: /**
722: * Returns <code>String</code> representation of this node.
723: *
724: * @return <code>String</code> representation of this node.
725: */
726: public String toString() {
727: return toString(Indent.DEFAULT_TAB);
728: }
729:
730: /**
731: * Returns <code>String</code> representation of this node.
732: *
733: * @param tab tab for node indentation.
734: *
735: * @return <code>String</code> representation of this node.
736: */
737: public String toString(String tab) {
738: StringBuffer sb = new StringBuffer();
739: this .allToString(sb, new Indent(0, tab));
740: return sb.toString();
741: }
742:
743: /**
744: * Method beginToString should be redefined in extended classes.
745: * Each type of node has its own <code>beginToString and
746: * <code>endToString</code>. This was added to support
747: * writing of the xml file. The <code>Element</code>
748: * type of node: it writes the beginning tag, then calls
749: * the child's <code>toString</code>, and then writes the ending tag.
750: *
751: * @param sb string buffer to add resulting string.
752: * @param indent used in formating the output.
753: */
754: protected void beginToString(StringBuffer sb, Indent indent) {
755: }
756:
757: /**
758: * Method endToString should be redefined in extended classes.
759: * Each type of node has its own <code>beginToString and
760: * <code>endToString</code>. This was added to support
761: * writing of the xml file. The <code>Element</code>
762: * type of node: it writes the beginning tag, then calls
763: * the child's <code>toString</code>, and then writes the ending tag.
764: *
765: * @param sb string buffer to add resulting string.
766: * @param indent used in formating the output.
767: */
768: protected void endToString(StringBuffer sb, Indent indent) {
769: }
770:
771: private void allToString(StringBuffer sb, Indent indent) {
772: this .beginToString(sb, indent);
773: Node child = getFirstChild();
774: while (child != null) {
775: ((NodeImpl) child).allToString(sb, indent);
776: child = child.getNextSibling();
777: }
778: this .endToString(sb, indent);
779: }
780:
781: /* METHODS FROM INTERFACE IN JDK1.5 */
782:
783: public short compareDocumentPosition(Node other)
784: throws DOMException {
785: // TODO Auto-generated method stub
786: return 0;
787: }
788:
789: public String getBaseURI() {
790: // TODO Auto-generated method stub
791: return null;
792: }
793:
794: public Object getFeature(String feature, String version) {
795: // TODO Auto-generated method stub
796: return null;
797: }
798:
799: public String getTextContent() throws DOMException {
800: // TODO Auto-generated method stub
801: return null;
802: }
803:
804: public Object getUserData(String key) {
805: // TODO Auto-generated method stub
806: return null;
807: }
808:
809: public boolean isDefaultNamespace(String namespaceURI) {
810: // TODO Auto-generated method stub
811: return false;
812: }
813:
814: public boolean isEqualNode(Node arg) {
815: // TODO Auto-generated method stub
816: return false;
817: }
818:
819: public boolean isSameNode(Node other) {
820: // TODO Auto-generated method stub
821: return false;
822: }
823:
824: public String lookupNamespaceURI(String prefix) {
825: // TODO Auto-generated method stub
826: return null;
827: }
828:
829: public String lookupPrefix(String namespaceURI) {
830: // TODO Auto-generated method stub
831: return null;
832: }
833:
834: public void setTextContent(String textContent) throws DOMException {
835: // TODO Auto-generated method stub
836:
837: }
838:
839: public Object setUserData(String key, Object data,
840: UserDataHandler handler) {
841: // TODO Auto-generated method stub
842: return null;
843: }
844: }
|