Merge from the pain train
[official-gcc.git] / libjava / gnu / xml / dom / DomNode.java
blob20a62c53ba52f1f45325a6280038553593c0779c
1 /* DomNode.java --
2 Copyright (C) 1999,2000,2001,2004 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
38 package gnu.xml.dom;
40 import java.util.HashMap;
41 import java.util.Iterator;
42 import java.util.Map;
44 import org.w3c.dom.Document;
45 import org.w3c.dom.DOMException;
46 import org.w3c.dom.DOMImplementation;
47 import org.w3c.dom.NamedNodeMap;
48 import org.w3c.dom.Node;
49 import org.w3c.dom.NodeList;
50 import org.w3c.dom.Text;
51 import org.w3c.dom.UserDataHandler;
52 import org.w3c.dom.events.DocumentEvent;
53 import org.w3c.dom.events.Event;
54 import org.w3c.dom.events.EventException;
55 import org.w3c.dom.events.EventListener;
56 import org.w3c.dom.events.EventTarget;
57 import org.w3c.dom.events.MutationEvent;
58 import org.w3c.dom.traversal.NodeFilter;
59 import org.w3c.dom.traversal.NodeIterator;
61 /**
62 * <p> "Node", "EventTarget", and "DocumentEvent" implementation.
63 * This provides most of the core DOM functionality; only more
64 * specialized features are provided by subclasses. Those subclasses may
65 * have some particular constraints they must implement, by overriding
66 * methods defined here. Such constraints are noted here in the method
67 * documentation. </p>
69 * <p> Note that you can create events with type names prefixed with "USER-",
70 * and pass them through this DOM. This lets you use the DOM event scheme
71 * for application specific purposes, although you must use a predefined event
72 * structure (such as MutationEvent) to pass data along with those events.
73 * Test for existence of this feature with the "USER-Events" DOM feature
74 * name.</p>
76 * <p> Other kinds of events you can send include the "html" events,
77 * like "load", "unload", "abort", "error", and "blur"; and the mutation
78 * events. If this DOM has been compiled with mutation event support
79 * enabled, it will send mutation events when you change parts of the
80 * tree; otherwise you may create and send such events yourself, but
81 * they won't be generated by the DOM itself. </p>
83 * <p> Note that there is a namespace-aware name comparison method,
84 * <em>nameAndTypeEquals</em>, which compares the names (and types) of
85 * two nodes in conformance with the "Namespaces in XML" specification.
86 * While mostly intended for use with elements and attributes, this should
87 * also be helpful for ProcessingInstruction nodes and some others which
88 * do not have namespace URIs.
90 * @author David Brownell
91 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
93 public abstract class DomNode
94 implements Node, NodeList, EventTarget, DocumentEvent, Cloneable, Comparable
97 // package private
98 //final static String xmlNamespace = "http://www.w3.org/XML/1998/namespace";
99 //final static String xmlnsURI = "http://www.w3.org/2000/xmlns/";
101 // tunable
102 // NKIDS_* affects arrays of children (which grow)
103 // (currently) fixed size:
104 // ANCESTORS_* is for event capture/bubbling, # ancestors
105 // NOTIFICATIONS_* is for per-node event delivery, # events
106 private static final int NKIDS_DELTA = 8;
107 private static final int ANCESTORS_INIT = 20;
108 private static final int NOTIFICATIONS_INIT = 10;
110 // tunable: enable mutation events or not? Enabling it costs about
111 // 10-15% in DOM construction time, last time it was measured.
113 // package private !!!
114 static final boolean reportMutations = true;
116 // locking protocol changeable only within this class
117 private static final Object lockNode = new Object();
119 // NON-FINAL class data
121 // Optimize event dispatch by not allocating memory each time
122 private static boolean dispatchDataLock;
123 private static DomNode[] ancestors = new DomNode[ANCESTORS_INIT];
124 private static ListenerRecord[] notificationSet
125 = new ListenerRecord[NOTIFICATIONS_INIT];
127 // Ditto for the (most common) event object itself!
128 private static boolean eventDataLock;
129 private static DomEvent.DomMutationEvent mutationEvent
130 = new DomEvent.DomMutationEvent(null);
133 // PER-INSTANCE DATA
136 DomDocument owner;
137 DomNode parent; // parent node;
138 DomNode previous; // previous sibling node
139 DomNode next; // next sibling node
140 DomNode first; // first child node
141 DomNode last; // last child node
142 int index; // index of this node in its parent's children
143 int depth; // depth of the node in the document
144 int length; // number of children
145 final short nodeType;
147 // Bleech ... "package private" so a builder can populate entity refs.
148 // writable during construction. DOM spec is nasty.
149 boolean readonly;
151 // event registrations
152 private ListenerRecord[] listeners;
153 private int nListeners;
155 // DOM Level 3 userData dictionary.
156 private HashMap userData;
157 private HashMap userDataHandlers;
160 // Some of the methods here are declared 'final' because
161 // knowledge about their implementation is built into this
162 // class -- for both integrity and performance.
166 * Reduces space utilization for this node.
168 public void compact()
170 if (listeners != null && listeners.length != nListeners)
172 if (nListeners == 0)
174 listeners = null;
176 else
178 ListenerRecord[] l = new ListenerRecord[nListeners];
179 System.arraycopy(listeners, 0, l, 0, nListeners);
180 listeners = l;
186 * Constructs a node and associates it with its owner. Only
187 * Document and DocumentType nodes may be created with no owner,
188 * and DocumentType nodes get an owner as soon as they are
189 * associated with a document.
191 protected DomNode(short nodeType, DomDocument owner)
193 this.nodeType = nodeType;
195 if (owner == null)
197 // DOM calls never go down this path
198 if (nodeType != DOCUMENT_NODE && nodeType != DOCUMENT_TYPE_NODE)
200 throw new IllegalArgumentException ("no owner!");
203 this.owner = owner;
208 * <b>DOM L1</b>
209 * Returns null; Element subclasses must override this method.
211 public NamedNodeMap getAttributes()
213 return null;
217 * <b>DOM L2></b>
218 * Returns true iff this is an element node with attributes.
220 public boolean hasAttributes()
222 return false;
226 * <b>DOM L1</b>
227 * Returns a list, possibly empty, of the children of this node.
228 * In this implementation, to conserve memory, nodes are the same
229 * as their list of children. This can have ramifications for
230 * subclasses, which may need to provide their own getLength method
231 * for reasons unrelated to the NodeList method of the same name.
233 public NodeList getChildNodes()
235 return this;
239 * <b>DOM L1</b>
240 * Returns the first child of this node, or null if there are none.
242 public Node getFirstChild()
244 return first;
248 * <b>DOM L1</b>
249 * Returns the last child of this node, or null if there are none.
251 public Node getLastChild()
253 return last;
257 * <b>DOM L1</b>
258 * Returns true if this node has children.
260 public boolean hasChildNodes()
262 return length != 0;
267 * Exposes the internal "readonly" flag. In DOM, children of
268 * entities and entity references are readonly, as are the
269 * objects associated with DocumentType objets.
271 public final boolean isReadonly()
273 return readonly;
277 * Sets the internal "readonly" flag so this subtree can't be changed.
278 * Subclasses need to override this method for any associated content
279 * that's not a child node, such as an element's attributes or the
280 * (few) declarations associated with a DocumentType.
282 public void makeReadonly()
284 readonly = true;
285 for (DomNode child = first; child != null; child = child.next)
287 child.makeReadonly();
292 * Used to adopt a node to a new document.
294 void setOwner(DomDocument doc)
296 this.owner = doc;
297 for (DomNode ctx = first; ctx != null; ctx = ctx.next)
299 ctx.setOwner(doc);
303 // just checks the node for inclusion -- may be called many
304 // times (docfrag) before anything is allowed to change
305 private void checkMisc(DomNode child)
307 if (readonly && !owner.building)
309 throw new DomDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR,
310 null, this, 0);
312 for (DomNode ctx = this; ctx != null; ctx = ctx.parent)
314 if (child == ctx)
316 throw new DomDOMException(DOMException.HIERARCHY_REQUEST_ERR,
317 "can't make ancestor into a child",
318 this, 0);
322 DomDocument owner = (nodeType == DOCUMENT_NODE) ? (DomDocument) this :
323 this.owner;
324 DomDocument childOwner = child.owner;
325 short childNodeType = child.nodeType;
327 if (childOwner != owner)
329 // new in DOM L2, this case -- patch it up later, in reparent()
330 if (!(childNodeType == DOCUMENT_TYPE_NODE && childOwner == null))
332 throw new DomDOMException(DOMException.WRONG_DOCUMENT_ERR,
333 null, child, 0);
337 // enforce various structural constraints
338 switch (nodeType)
340 case DOCUMENT_NODE:
341 switch (childNodeType)
343 case ELEMENT_NODE:
344 case PROCESSING_INSTRUCTION_NODE:
345 case COMMENT_NODE:
346 case DOCUMENT_TYPE_NODE:
347 return;
349 break;
351 case ATTRIBUTE_NODE:
352 switch (childNodeType)
354 case TEXT_NODE:
355 case ENTITY_REFERENCE_NODE:
356 return;
358 break;
360 case DOCUMENT_FRAGMENT_NODE:
361 case ENTITY_REFERENCE_NODE:
362 case ELEMENT_NODE:
363 case ENTITY_NODE:
364 switch (childNodeType)
366 case ELEMENT_NODE:
367 case TEXT_NODE:
368 case COMMENT_NODE:
369 case PROCESSING_INSTRUCTION_NODE:
370 case CDATA_SECTION_NODE:
371 case ENTITY_REFERENCE_NODE:
372 return;
374 break;
376 if (owner.checkingWellformedness)
378 throw new DomDOMException(DOMException.HIERARCHY_REQUEST_ERR,
379 "can't append " +
380 nodeTypeToString(childNodeType) +
381 " to node of type " +
382 nodeTypeToString(nodeType),
383 this, 0);
387 // Here's hoping a good optimizer will detect the case when the
388 // next several methods are never called, and won't allocate
389 // object code space of any kind. (Case: not reporting any
390 // mutation events. We can also remove some static variables
391 // listed above.)
393 private void insertionEvent(DomEvent.DomMutationEvent event,
394 DomNode target)
396 if (owner == null || owner.building)
398 return;
400 boolean doFree = false;
402 if (event == null)
404 event = getMutationEvent();
406 if (event != null)
408 doFree = true;
410 else
412 event = new DomEvent.DomMutationEvent(null);
414 event.initMutationEvent("DOMNodeInserted",
415 true /* bubbles */, false /* nocancel */,
416 this /* related */, null, null, null, (short) 0);
417 target.dispatchEvent(event);
419 // XXX should really visit every descendant of 'target'
420 // and sent a DOMNodeInsertedIntoDocument event to it...
421 // bleech, there's no way to keep that acceptably fast.
423 if (doFree)
425 event.target = null;
426 event.relatedNode = null;
427 event.currentNode = null;
428 eventDataLock = false;
429 } // else we created work for the GC
432 private void removalEvent(DomEvent.DomMutationEvent event,
433 DomNode target)
435 if (owner == null || owner.building)
437 return;
439 boolean doFree = false;
441 if (event == null)
443 event = getMutationEvent();
445 if (event != null)
447 doFree = true;
449 else
451 event = new DomEvent.DomMutationEvent(null);
453 event.initMutationEvent("DOMNodeRemoved",
454 true /* bubbles */, false /* nocancel */,
455 this /* related */, null, null, null, (short) 0);
456 target.dispatchEvent(event);
458 // XXX should really visit every descendant of 'target'
459 // and sent a DOMNodeRemovedFromDocument event to it...
460 // bleech, there's no way to keep that acceptably fast.
462 event.target = null;
463 event.relatedNode = null;
464 event.currentNode = null;
465 if (doFree)
467 eventDataLock = false;
469 // else we created more work for the GC
473 // Avoid creating lots of memory management work, by using a simple
474 // allocation strategy for the mutation event objects that get used
475 // at least once per tree modification. We can't use stack allocation,
476 // so we do the next simplest thing -- more or less, static allocation.
477 // Concurrent notifications should be rare, anyway.
479 // Returns the preallocated object, which needs to be carefully freed,
480 // or null to indicate the caller needs to allocate their own.
482 static private DomEvent.DomMutationEvent getMutationEvent()
484 synchronized (lockNode)
486 if (eventDataLock)
488 return null;
490 eventDataLock = true;
491 return mutationEvent;
495 // NOTE: this is manually inlined in the insertion
496 // and removal event methods above; change in sync.
497 static private void freeMutationEvent()
499 // clear fields to enable GC
500 mutationEvent.clear();
501 eventDataLock = false;
504 void setDepth(int depth)
506 this.depth = depth;
507 for (DomNode ctx = first; ctx != null; ctx = ctx.next)
509 ctx.setDepth(depth + 1);
514 * <b>DOM L1</b>
515 * Appends the specified node to this node's list of children.
516 * Document subclasses must override this to enforce the restrictions
517 * that there be only one element and document type child.
519 * <p> Causes a DOMNodeInserted mutation event to be reported.
520 * Will first cause a DOMNodeRemoved event to be reported if the
521 * parameter already has a parent. If the new child is a document
522 * fragment node, both events will be reported for each child of
523 * the fragment; the order in which children are removed and
524 * inserted is implementation-specific.
526 * <p> If this DOM has been compiled without mutation event support,
527 * these events will not be reported.
529 public Node appendChild(Node newChild)
533 DomNode child = (DomNode) newChild;
535 if (child.nodeType == DOCUMENT_FRAGMENT_NODE)
537 // Append all nodes in the fragment to this node
538 for (DomNode ctx = child.first; ctx != null; ctx = ctx.next)
540 checkMisc(ctx);
542 for (DomNode ctx = child.first; ctx != null; )
544 DomNode ctxNext = ctx.next;
545 appendChild(ctx);
546 ctx = ctxNext;
549 else
551 checkMisc(child);
552 if (child.parent != null)
554 child.parent.removeChild(child);
556 child.parent = this;
557 child.index = length++;
558 child.setDepth(depth + 1);
559 child.next = null;
560 if (last == null)
562 first = child;
563 child.previous = null;
565 else
567 last.next = child;
568 child.previous = last;
570 last = child;
572 if (reportMutations)
574 insertionEvent(null, child);
578 return child;
580 catch (ClassCastException e)
582 throw new DomDOMException(DOMException.WRONG_DOCUMENT_ERR,
583 null, newChild, 0);
588 * <b>DOM L1</b>
589 * Inserts the specified node in this node's list of children.
590 * Document subclasses must override this to enforce the restrictions
591 * that there be only one element and document type child.
593 * <p> Causes a DOMNodeInserted mutation event to be reported. Will
594 * first cause a DOMNodeRemoved event to be reported if the newChild
595 * parameter already has a parent. If the new child is a document
596 * fragment node, both events will be reported for each child of
597 * the fragment; the order in which children are removed and inserted
598 * is implementation-specific.
600 * <p> If this DOM has been compiled without mutation event support,
601 * these events will not be reported.
603 public Node insertBefore(Node newChild, Node refChild)
605 if (refChild == null)
607 return appendChild(newChild);
612 DomNode child = (DomNode) newChild;
613 DomNode ref = (DomNode) refChild;
615 if (child.nodeType == DOCUMENT_FRAGMENT_NODE)
617 // Append all nodes in the fragment to this node
618 for (DomNode ctx = child.first; ctx != null; ctx = ctx.next)
620 checkMisc(ctx);
622 for (DomNode ctx = child.first; ctx != null; )
624 DomNode ctxNext = ctx.next;
625 insertBefore(ctx, ref);
626 ctx = ctxNext;
629 else
631 checkMisc(child);
632 if (ref == null || ref.parent != this)
634 throw new DomDOMException(DOMException.NOT_FOUND_ERR,
635 null, ref, 0);
637 if (ref == child)
639 throw new DomDOMException(DOMException.HIERARCHY_REQUEST_ERR,
640 "can't insert node before itself",
641 ref, 0);
644 if (child.parent != null)
646 child.parent.removeChild(child);
648 child.parent = this;
649 int i = ref.index;
650 child.setDepth(depth + 1);
651 child.next = ref;
652 if (ref.previous != null)
654 ref.previous.next = child;
656 child.previous = ref.previous;
657 ref.previous = child;
658 if (first == ref)
660 first = child;
662 // index renumbering
663 for (DomNode ctx = child; ctx != null; ctx = ctx.next)
665 ctx.index = i++;
668 if (reportMutations)
670 insertionEvent(null, child);
674 return child;
676 catch (ClassCastException e)
678 throw new DomDOMException(DOMException.WRONG_DOCUMENT_ERR,
679 null, newChild, 0);
684 * <b>DOM L1</b>
685 * Replaces the specified node in this node's list of children.
686 * Document subclasses must override this to test the restrictions
687 * that there be only one element and document type child.
689 * <p> Causes DOMNodeRemoved and DOMNodeInserted mutation event to be
690 * reported. Will cause another DOMNodeRemoved event to be reported if
691 * the newChild parameter already has a parent. These events may be
692 * delivered in any order, except that the event reporting removal
693 * from such an existing parent will always be delivered before the
694 * event reporting its re-insertion as a child of some other node.
695 * The order in which children are removed and inserted is implementation
696 * specific.
698 * <p> If your application needs to depend on the in which those removal
699 * and insertion events are delivered, don't use this API. Instead,
700 * invoke the removeChild and insertBefore methods directly, to guarantee
701 * a specific delivery order. Similarly, don't use document fragments,
702 * Otherwise your application code may not work on a DOM which implements
703 * this method differently.
705 * <p> If this DOM has been compiled without mutation event support,
706 * these events will not be reported.
708 public Node replaceChild(Node newChild, Node refChild)
712 DomNode child = (DomNode) newChild;
713 DomNode ref = (DomNode) refChild;
715 DomEvent.DomMutationEvent event = getMutationEvent();
716 boolean doFree = (event != null);
718 if (child.nodeType == DOCUMENT_FRAGMENT_NODE)
720 // Append all nodes in the fragment to this node
721 for (DomNode ctx = child.first; ctx != null; ctx = ctx.next)
723 checkMisc(ctx);
725 if (ref == null || ref.parent != this)
727 throw new DomDOMException(DOMException.NOT_FOUND_ERR,
728 null, ref, 0);
731 if (reportMutations)
733 removalEvent(event, ref);
735 length--;
736 length += child.length;
738 if (child.length == 0)
740 // Removal
741 if (ref.previous != null)
743 ref.previous.next = ref.next;
745 if (ref.next != null)
747 ref.next.previous = ref.previous;
749 if (first == ref)
751 first = ref.next;
753 if (last == ref)
755 last = ref.previous;
758 else
760 int i = ref.index;
761 for (DomNode ctx = child.first; ctx != null; ctx = ctx.next)
763 // Insertion
764 ctx.parent = this;
765 ctx.index = i++;
766 ctx.setDepth(ref.depth);
767 if (ctx == child.first)
769 ctx.previous = ref.previous;
771 if (ctx == child.last)
773 ctx.next = ref.next;
776 if (first == ref)
778 first = child.first;
780 if (last == ref)
782 last = child.last;
786 else
788 checkMisc(child);
789 if (ref == null || ref.parent != this)
791 throw new DomDOMException(DOMException.NOT_FOUND_ERR,
792 null, ref, 0);
795 if (reportMutations)
797 removalEvent(event, ref);
800 if (child.parent != null)
802 child.parent.removeChild(child);
804 child.parent = this;
805 child.index = ref.index;
806 child.setDepth(ref.depth);
807 if (ref.previous != null)
809 ref.previous.next = child;
811 child.previous = ref.previous;
812 if (ref.next != null)
814 ref.next.previous = child;
816 child.next = ref.next;
817 if (first == ref)
819 first = child;
821 if (last == ref)
823 last = child;
826 if (reportMutations)
828 insertionEvent(event, child);
830 if (doFree)
832 freeMutationEvent();
835 ref.parent = null;
836 ref.index = 0;
837 ref.setDepth(0);
838 ref.previous = null;
839 ref.next = null;
841 return ref;
843 catch (ClassCastException e)
845 throw new DomDOMException(DOMException.WRONG_DOCUMENT_ERR,
846 null, newChild, 0);
851 * <b>DOM L1</b>
852 * Removes the specified child from this node's list of children,
853 * or else reports an exception.
855 * <p> Causes a DOMNodeRemoved mutation event to be reported.
857 * <p> If this DOM has been compiled without mutation event support,
858 * these events will not be reported.
860 public Node removeChild(Node refChild)
864 DomNode ref = (DomNode) refChild;
866 if (ref == null || ref.parent != this)
868 throw new DomDOMException(DOMException.NOT_FOUND_ERR,
869 null, ref, 0);
871 if (readonly && !owner.building)
873 throw new DomDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR,
874 null, this, 0);
877 for (DomNode child = first; child != null; child = child.next)
879 if (child == ref)
881 if (reportMutations)
883 removalEvent(null, child);
886 length--;
887 if (ref.previous != null)
889 ref.previous.next = ref.next;
891 if (ref.next != null)
893 ref.next.previous = ref.previous;
895 if (first == ref)
897 first = ref.next;
899 if (last == ref)
901 last = ref.previous;
903 // renumber indices
904 int i = 0;
905 for (DomNode ctx = first; ctx != null; ctx = ctx.next)
907 ctx.index = i++;
909 ref.parent = null;
910 ref.setDepth(0);
911 ref.index = 0;
912 ref.previous = null;
913 ref.next = null;
915 return ref;
918 throw new DomDOMException(DOMException.NOT_FOUND_ERR,
919 "that's no child of mine", refChild, 0);
921 catch (ClassCastException e)
923 throw new DomDOMException(DOMException.WRONG_DOCUMENT_ERR,
924 null, refChild, 0);
929 * <b>DOM L1 (NodeList)</b>
930 * Returns the item with the specified index in this NodeList,
931 * else null.
933 public Node item(int index)
935 DomNode child = first;
936 int count = 0;
937 while (child != null && count < index)
939 child = child.next;
940 count++;
942 return child;
946 * <b>DOM L1 (NodeList)</b>
947 * Returns the number of elements in this NodeList.
948 * (Note that many interfaces have a "Length" property, not just
949 * NodeList, and if a node subtype must implement one of those,
950 * it will also need to override getChildNodes.)
952 public int getLength()
954 return length;
958 * Minimize extra space consumed by this node to hold children and event
959 * listeners.
961 public void trimToSize()
963 if (listeners != null && listeners.length != nListeners)
965 ListenerRecord[] newKids = new ListenerRecord[length];
966 System.arraycopy(listeners, 0, newKids, 0, nListeners);
967 listeners = newKids;
972 * <b>DOM L1</b>
973 * Returns the previous sibling, if one is known.
975 public Node getNextSibling()
977 return next;
981 * <b>DOM L1</b>
982 * Returns the previous sibling, if one is known.
984 public Node getPreviousSibling()
986 return previous;
990 * <b>DOM L1</b>
991 * Returns the parent node, if one is known.
993 public Node getParentNode()
995 return parent;
999 * <b>DOM L2</b>
1000 * Consults the DOM implementation to determine if the requested
1001 * feature is supported. DocumentType subclasses must override
1002 * this method, and associate themselves directly with the
1003 * DOMImplementation node used. (This method relies on being able
1004 * to access the DOMImplementation from the owner document, but
1005 * DocumentType nodes can be created without an owner.)
1007 public boolean isSupported(String feature, String version)
1009 Document doc = owner;
1010 DOMImplementation impl = null;
1012 if (doc == null && nodeType == DOCUMENT_NODE)
1014 doc = (Document) this;
1017 if (doc == null)
1019 // possible for DocumentType
1020 throw new IllegalStateException ("unbound ownerDocument");
1023 impl = doc.getImplementation();
1024 return impl.hasFeature(feature, version);
1028 * <b>DOM L1 (modified in L2)</b>
1029 * Returns the owner document. This is only null for Document nodes,
1030 * and (new in L2) for DocumentType nodes which have not yet been
1031 * associated with the rest of their document.
1033 final public Document getOwnerDocument()
1035 return owner;
1039 * <b>DOM L1</b>
1040 * Does nothing; this must be overridden (along with the
1041 * getNodeValue method) for nodes with a non-null defined value.
1043 public void setNodeValue(String value)
1048 * <b>DOM L1</b>
1049 * Returns null; this must be overridden for nodes types with
1050 * a defined value, along with the setNodeValue method.
1052 public String getNodeValue()
1054 return null;
1057 /** This forces GCJ compatibility.
1058 * Without this method GCJ is unable to compile to byte code.
1060 public final short getNodeType()
1062 return nodeType;
1065 /** This forces GCJ compatibility.
1066 * Without this method GCJ seems unable to natively compile GNUJAXP.
1068 public abstract String getNodeName();
1071 * <b>DOM L2</b>
1072 * Does nothing; this must be overridden (along with the
1073 * getPrefix method) for element and attribute nodes.
1075 public void setPrefix(String prefix)
1080 * <b>DOM L2</b>
1081 * Returns null; this must be overridden for element and
1082 * attribute nodes.
1084 public String getPrefix()
1086 return null;
1090 * <b>DOM L2</b>
1091 * Returns null; this must be overridden for element and
1092 * attribute nodes.
1094 public String getNamespaceURI()
1096 return null;
1100 * <b>DOM L2</b>
1101 * Returns the node name; this must be overridden for element and
1102 * attribute nodes.
1104 public String getLocalName()
1106 return null;
1110 * <b>DOM L1</b>
1111 * Returns a clone of this node which optionally includes cloned
1112 * versions of child nodes. Clones are always mutable, except for
1113 * entity reference nodes.
1115 public Node cloneNode(boolean deep)
1117 DomNode node = (DomNode) clone();
1119 if (deep)
1121 DomDocument doc = (nodeType == DOCUMENT_NODE) ?
1122 (DomDocument) node : node.owner;
1123 for (DomNode ctx = first; ctx != null; ctx = ctx.next)
1125 DomNode newChild = (DomNode) ctx.cloneNode(deep);
1126 newChild.setOwner(doc);
1127 node.appendChild(newChild);
1131 if (nodeType == ENTITY_REFERENCE_NODE)
1133 node.makeReadonly();
1135 notifyUserDataHandlers(UserDataHandler.NODE_CLONED, this, node);
1136 return node;
1139 void notifyUserDataHandlers(short op, Node src, Node dst)
1141 if (userDataHandlers != null)
1143 for (Iterator i = userDataHandlers.entrySet().iterator(); i.hasNext(); )
1145 Map.Entry entry = (Map.Entry) i.next();
1146 String key = (String) entry.getKey();
1147 UserDataHandler handler = (UserDataHandler) entry.getValue();
1148 Object data = userData.get(key);
1149 handler.handle(op, key, data, src, dst);
1155 * Clones this node; roughly equivalent to cloneNode(false).
1156 * Element subclasses must provide a new implementation which
1157 * invokes this method to handle the basics, and then arranges
1158 * to clone any element attributes directly. Attribute subclasses
1159 * must make similar arrangements, ensuring that existing ties to
1160 * elements are broken by cloning.
1162 public Object clone()
1166 DomNode node = (DomNode) super.clone();
1168 node.parent = null;
1169 node.depth = 0;
1170 node.index = 0;
1171 node.length = 0;
1172 node.first = null;
1173 node.last = null;
1174 node.previous = null;
1175 node.next = null;
1177 node.readonly = false;
1178 node.listeners = null;
1179 node.nListeners = 0;
1180 return node;
1183 catch (CloneNotSupportedException x)
1185 throw new Error("clone didn't work");
1189 // the elements-by-tagname stuff is needed for both
1190 // elements and documents ... this is in lieu of a
1191 // common base class between Node and NodeNS.
1194 * <b>DOM L1</b>
1195 * Creates a NodeList giving array-style access to elements with
1196 * the specified name. Access is fastest if indices change by
1197 * small values, and the DOM is not modified.
1199 public NodeList getElementsByTagName(String tag)
1201 return new ShadowList(null, tag);
1205 * <b>DOM L2</b>
1206 * Creates a NodeList giving array-style access to elements with
1207 * the specified namespace and local name. Access is fastest if
1208 * indices change by small values, and the DOM is not modified.
1210 public NodeList getElementsByTagNameNS(String namespace, String local)
1212 return new ShadowList(namespace, local);
1217 // This shadow class is GC-able even when the live list it shadows
1218 // can't be, because of event registration hookups. Its finalizer
1219 // makes that live list become GC-able.
1221 final class ShadowList
1222 implements NodeList
1225 private LiveNodeList liveList;
1227 ShadowList(String ns, String local)
1229 liveList = new LiveNodeList(ns, local);
1232 public void finalize()
1234 liveList.detach();
1235 liveList = null;
1238 public Node item(int index)
1240 return liveList.item(index);
1243 public int getLength()
1245 return liveList.getLength();
1249 final class LiveNodeList
1250 implements NodeList, EventListener, NodeFilter
1253 private final boolean matchAnyURI;
1254 private final boolean matchAnyName;
1255 private final String elementURI;
1256 private final String elementName;
1258 private DomIterator current;
1259 private int lastIndex;
1261 LiveNodeList(String uri, String name)
1263 elementURI = uri;
1264 elementName = name;
1265 matchAnyURI = "*".equals(uri);
1266 matchAnyName = "*".equals(name);
1268 DomNode.this.addEventListener("DOMNodeInserted", this, true);
1269 DomNode.this.addEventListener("DOMNodeRemoved", this, true);
1272 void detach()
1274 current.detach();
1275 current = null;
1277 DomNode.this.removeEventListener("DOMNodeInserted", this, true);
1278 DomNode.this.removeEventListener("DOMNodeRemoved", this, true);
1281 public short acceptNode(Node element)
1283 if (element == DomNode.this)
1285 return FILTER_SKIP;
1288 // use namespace-aware matching ...
1289 if (elementURI != null)
1291 if (!(matchAnyURI
1292 || elementURI.equals(element.getNamespaceURI())))
1294 return FILTER_SKIP;
1296 if (!(matchAnyName
1297 || elementName.equals(element.getLocalName())))
1299 return FILTER_SKIP;
1302 // ... or qName-based kind.
1304 else
1306 if (!(matchAnyName
1307 || elementName.equals(element.getNodeName())))
1309 return FILTER_SKIP;
1312 return FILTER_ACCEPT;
1315 private DomIterator createIterator()
1317 return new DomIterator(DomNode.this,
1318 NodeFilter.SHOW_ELEMENT,
1319 this, /* filter */
1320 true /* expand entity refs */
1324 public void handleEvent(Event e)
1326 MutationEvent mutation = (MutationEvent) e;
1327 Node related = mutation.getRelatedNode();
1329 // XXX if it's got children ... check all kids too, they
1330 // will invalidate our saved index
1332 if (related.getNodeType() != Node.ELEMENT_NODE ||
1333 related.getNodeName() != elementName ||
1334 related.getNamespaceURI() != elementURI)
1336 return;
1339 current = null;
1342 public Node item(int index)
1344 if (current == null)
1346 current = createIterator();
1347 lastIndex = -1;
1350 // last node or before? go backwards
1351 if (index <= lastIndex) {
1352 while (index != lastIndex) {
1353 current.previousNode ();
1354 lastIndex--;
1356 Node ret = current.previousNode ();
1357 current = null;
1358 return ret;
1361 // somewhere after last node
1362 while (++lastIndex != index)
1363 current.nextNode ();
1364 Node ret = current.nextNode ();
1365 current = null;
1366 return ret;
1369 public int getLength()
1371 int retval = 0;
1372 NodeIterator iter = createIterator();
1374 while (iter.nextNode() != null)
1376 retval++;
1378 current = null;
1379 return retval;
1385 // EventTarget support
1387 static final class ListenerRecord
1390 String type;
1391 EventListener listener;
1392 boolean useCapture;
1394 // XXX use JDK 1.2 java.lang.ref.WeakReference to listener,
1395 // and we can both get rid of "shadow" classes and remove
1396 // the need for applications to apply similar trix ... but
1397 // JDK 1.2 support isn't generally available yet
1399 ListenerRecord(String type, EventListener listener, boolean useCapture)
1401 this.type = type.intern();
1402 this.listener = listener;
1403 this.useCapture = useCapture;
1406 boolean equals(ListenerRecord rec)
1408 return listener == rec.listener
1409 && useCapture == rec.useCapture
1410 && type == rec.type;
1416 * <b>DOM L2 (Events)</b>
1417 * Returns an instance of the specified type of event object.
1418 * Understands about DOM Mutation, HTML, and UI events.
1420 * <p>If the name of the event type begins with "USER-", then an object
1421 * implementing the "Event" class will be returned; this provides a
1422 * limited facility for application-defined events to use the DOM event
1423 * infrastructure. Alternatively, use one of the standard DOM event
1424 * classes and initialize it using use such a "USER-" event type name;
1425 * or defin, instantiate, and initialize an application-specific subclass
1426 * of DomEvent and pass that to dispatchEvent().
1428 * @param eventType Identifies the particular DOM feature module
1429 * defining the type of event, such as "MutationEvents".
1430 * <em>The event "name" is a different kind of "type".</em>
1432 public Event createEvent(String eventType)
1434 eventType = eventType.toLowerCase();
1436 if ("mutationevents".equals(eventType))
1438 return new DomEvent.DomMutationEvent(null);
1441 if ("htmlevents".equals(eventType)
1442 || "events".equals(eventType)
1443 || "user-events".equals(eventType))
1445 return new DomEvent(null);
1448 if ("uievents".equals(eventType))
1450 return new DomEvent.DomUIEvent(null);
1453 // mouse events
1455 throw new DomDOMException(DOMException.NOT_SUPPORTED_ERR,
1456 eventType, null, 0);
1460 * <b>DOM L2 (Events)</b>
1461 * Registers an event listener's interest in a class of events.
1463 public final void addEventListener(String type,
1464 EventListener listener,
1465 boolean useCapture)
1467 if (listeners == null)
1469 listeners = new ListenerRecord[1];
1471 else if (nListeners == listeners.length)
1473 ListenerRecord[] newListeners =
1474 new ListenerRecord[listeners.length + NKIDS_DELTA];
1475 System.arraycopy(listeners, 0, newListeners, 0, nListeners);
1476 listeners = newListeners;
1479 // prune duplicates
1480 ListenerRecord record;
1482 record = new ListenerRecord(type, listener, useCapture);
1483 for (int i = 0; i < nListeners; i++)
1485 if (record.equals(listeners[i]))
1487 return;
1490 listeners [nListeners++] = record;
1493 // XXX this exception should be discarded from DOM
1495 // this class can be instantiated, unlike the one in the spec
1496 static final class DomEventException
1497 extends EventException
1500 DomEventException()
1502 super(UNSPECIFIED_EVENT_TYPE_ERR, "unspecified event type");
1508 * <b>DOM L2 (Events)</b>
1509 * Delivers an event to all relevant listeners, returning true if the
1510 * caller should perform their default action. Note that the event
1511 * must have been provided by the createEvent() method on this
1512 * class, else it can't be dispatched.
1514 * @see #createEvent
1516 * @exception NullPointerException When a null event is passed.
1517 * @exception ClassCastException When the event wasn't provided by
1518 * the createEvent method, or otherwise isn't a DomEvent.
1519 * @exception EventException If the event type wasn't specified
1521 public final boolean dispatchEvent(Event event)
1522 throws EventException
1524 DomEvent e = (DomEvent) event;
1525 DomNode[] ancestors = null;
1526 int ancestorMax = 0;
1527 boolean haveDispatchDataLock = false;
1529 if (e.type == null)
1531 throw new DomEventException();
1534 e.doDefault = true;
1535 e.target = this;
1538 // Typical case: one nonrecursive dispatchEvent call at a time
1539 // for this class. If that's our case, we can avoid allocating
1540 // garbage, which is overall a big win. Even with advanced GCs
1541 // that deal well with short-lived garbage, and wayfast allocators,
1542 // it still helps.
1544 // Remember -- EVERY mutation goes though here at least once.
1546 // When populating a DOM tree, trying to send mutation events is
1547 // the primary cost; this dominates the critical path.
1551 DomNode current;
1552 int index;
1553 boolean haveAncestorRegistrations = false;
1554 ListenerRecord[] notificationSet;
1555 int ancestorLen;
1557 synchronized (lockNode)
1559 if (!dispatchDataLock)
1561 haveDispatchDataLock = dispatchDataLock = true;
1562 notificationSet = DomNode.notificationSet;
1563 ancestors = DomNode.ancestors;
1565 else
1567 notificationSet = new ListenerRecord[NOTIFICATIONS_INIT];
1568 ancestors = new DomNode[ANCESTORS_INIT];
1570 ancestorLen = ancestors.length;
1573 // XXX autogrow ancestors ... based on statistics
1575 // Climb to the top of this subtree and handle capture, letting
1576 // each node (from the top down) capture until one stops it or
1577 // until we get to this one.
1579 for (index = 0, current = parent;
1580 current != null && index < ancestorLen;
1581 index++, current = current.parent)
1583 if (current.nListeners != 0)
1585 haveAncestorRegistrations = true;
1587 ancestors [index] = current;
1589 if (current != null)
1591 throw new RuntimeException("dispatchEvent capture stack size");
1594 ancestorMax = index;
1595 e.stop = false;
1597 if (haveAncestorRegistrations)
1599 e.eventPhase = Event.CAPTURING_PHASE;
1600 while (!e.stop && index-- > 0)
1602 current = ancestors [index];
1603 if (current.nListeners != 0)
1605 notifyNode(e, current, true, notificationSet);
1610 // Always deliver events to the target node (this)
1611 // unless stopPropagation was called. If we saw
1612 // no registrations yet (typical!), we never will.
1613 if (!e.stop && nListeners != 0)
1615 e.eventPhase = Event.AT_TARGET;
1616 notifyNode (e, this, false, notificationSet);
1618 else if (!haveAncestorRegistrations)
1620 e.stop = true;
1623 // If the event bubbles and propagation wasn't halted,
1624 // walk back up the ancestor list. Stop bubbling when
1625 // any bubbled event handler stops it.
1627 if (!e.stop && e.bubbles)
1629 e.eventPhase = Event.BUBBLING_PHASE;
1630 for (index = 0;
1631 !e.stop
1632 && index < ancestorMax
1633 && (current = ancestors[index]) != null;
1634 index++)
1636 if (current.nListeners != 0)
1638 notifyNode(e, current, false, notificationSet);
1642 e.eventPhase = 0;
1644 // Caller chooses whether to perform the default
1645 // action based on return from this method.
1646 return e.doDefault;
1649 finally
1651 if (haveDispatchDataLock)
1653 // synchronize to force write ordering
1654 synchronized (lockNode)
1656 // null out refs to ensure they'll be GC'd
1657 for (int i = 0; i < ancestorMax; i++)
1659 ancestors [i] = null;
1661 // notificationSet handled by notifyNode
1663 dispatchDataLock = false;
1669 private void notifyNode(DomEvent e,
1670 DomNode current,
1671 boolean capture,
1672 ListenerRecord[] notificationSet)
1674 int count = 0;
1676 // do any of this set of listeners get notified?
1677 for (int i = 0; i < current.nListeners; i++)
1679 ListenerRecord rec = current.listeners[i];
1681 if (rec.useCapture != capture)
1683 continue;
1685 if (!e.type.equals (rec.type))
1687 continue;
1689 if (count < notificationSet.length)
1691 notificationSet[count++] = rec;
1693 else
1694 // XXX fire up some cheap growth algorithm
1695 throw new RuntimeException("Event notification set size exceeded");
1698 // Notify just those listeners
1699 e.currentNode = current;
1700 for (int i = 0; i < count; i++)
1704 // Late in the DOM CR process (3rd or 4th CR?) the
1705 // removeEventListener spec became asymmetric with respect
1706 // to addEventListener ... effect is now immediate.
1707 for (int j = 0; j < current.nListeners; j++)
1709 if (current.listeners[j].equals(notificationSet[i]))
1711 notificationSet[i].listener.handleEvent(e);
1712 break;
1717 catch (Exception x)
1719 // ignore all exceptions
1721 notificationSet[i] = null; // free for GC
1726 * <b>DOM L2 (Events)</b>
1727 * Unregisters an event listener.
1729 public final void removeEventListener(String type,
1730 EventListener listener,
1731 boolean useCapture)
1733 for (int i = 0; i < nListeners; i++)
1735 if (listeners[i].listener != listener)
1737 continue;
1739 if (listeners[i].useCapture != useCapture)
1741 continue;
1743 if (!listeners[i].type.equals(type))
1745 continue;
1748 if (nListeners == 1)
1750 listeners = null;
1751 nListeners = 0;
1753 else
1755 for (int j = i + 1; j < nListeners; j++)
1757 listeners[i++] = listeners[j++];
1759 listeners[--nListeners] = null;
1761 break;
1763 // no exceptions reported
1767 * <b>DOM L1 (relocated in DOM L2)</b>
1768 * In this node and all contained nodes (including attributes if
1769 * relevant) merge adjacent text nodes. This is done while ignoring
1770 * text which happens to use CDATA delimiters).
1772 public final void normalize()
1774 // Suspend readonly status
1775 boolean saved = readonly;
1776 readonly = false;
1777 for (DomNode ctx = first; ctx != null; ctx = ctx.next)
1779 switch (ctx.nodeType)
1781 case TEXT_NODE:
1782 while (ctx.next != null && ctx.next.nodeType == TEXT_NODE)
1784 Text text = (Text) ctx;
1785 text.appendData(ctx.next.getNodeValue());
1786 removeChild(ctx.next);
1788 break;
1789 case ELEMENT_NODE:
1790 NamedNodeMap attrs = ctx.getAttributes();
1791 int len = attrs.getLength();
1792 for (int i = 0; i < len; i++)
1794 attrs.item(i).normalize();
1796 // Fall through
1797 case DOCUMENT_NODE:
1798 case DOCUMENT_FRAGMENT_NODE:
1799 case ATTRIBUTE_NODE:
1800 case ENTITY_REFERENCE_NODE:
1801 ctx.normalize();
1802 break;
1805 readonly = saved;
1809 * Returns true iff node types match, and either (a) both nodes have no
1810 * namespace and their getNodeName() values are the same, or (b) both
1811 * nodes have the same getNamespaceURI() and same getLocalName() values.
1813 * <p>Note that notion of a "Per-Element-Type" attribute name scope, as
1814 * found in a non-normative appendix of the XML Namespaces specification,
1815 * is not supported here. Your application must implement that notion,
1816 * typically by not bothering to check nameAndTypeEquals for attributes
1817 * without namespace URIs unless you already know their elements are
1818 * nameAndTypeEquals.
1820 public boolean nameAndTypeEquals(Node other)
1822 if (other == this)
1824 return true;
1826 // node types must match
1827 if (nodeType != other.getNodeType())
1829 return false;
1832 // if both have namespaces, do a "full" comparision
1833 // this is a "global" partition
1834 String ns1 = this.getNamespaceURI();
1835 String ns2 = other.getNamespaceURI();
1837 if (ns1 != null && ns2 != null)
1839 return ns1.equals(ns2) &&
1840 getLocalName().equals(other.getLocalName());
1843 // if neither has a namespace, this is a "no-namespace" name.
1844 if (ns1 == null && ns2 == null)
1846 if (!getNodeName().equals(other.getNodeName()))
1848 return false;
1850 // can test the non-normative "per-element-type" scope here.
1851 // if this is an attribute node and both nodes have been bound
1852 // to elements (!!), then return the nameAndTypeEquals()
1853 // comparison of those elements.
1854 return true;
1857 // otherwise they're unequal: one scoped, one not.
1858 return false;
1861 // DOM Level 3 methods
1863 public String getBaseURI()
1865 return (parent != null) ? parent.getBaseURI() : null;
1868 public short compareDocumentPosition(Node other)
1869 throws DOMException
1871 return (short) compareTo(other);
1875 * DOM nodes have a natural ordering: document order.
1877 public final int compareTo(Object other)
1879 if (other instanceof DomNode)
1881 DomNode n1 = this;
1882 DomNode n2 = (DomNode) other;
1883 if (n1.owner != n2.owner)
1885 return 0;
1887 int d1 = n1.depth, d2 = n2.depth;
1888 int delta = d1 - d2;
1889 while (d1 > d2)
1891 n1 = n1.parent;
1892 d1--;
1894 while (d2 > d1)
1896 n2 = n2.parent;
1897 d2--;
1899 int c = compareTo2(n1, n2);
1900 return (c != 0) ? c : delta;
1902 return 0;
1906 * Compare two nodes at the same depth.
1908 final int compareTo2(DomNode n1, DomNode n2)
1910 if (n1 == n2 || n1.depth == 0 || n2.depth == 0)
1912 return 0;
1914 int c = compareTo2(n1.parent, n2.parent);
1915 return (c != 0) ? c : n1.index - n2.index;
1918 public final String getTextContent()
1919 throws DOMException
1921 return getTextContent(true);
1924 final String getTextContent(boolean topLevel)
1925 throws DOMException
1927 switch (nodeType)
1929 case ELEMENT_NODE:
1930 case ENTITY_NODE:
1931 case ENTITY_REFERENCE_NODE:
1932 case DOCUMENT_FRAGMENT_NODE:
1933 StringBuffer buffer = new StringBuffer();
1934 for (DomNode ctx = first; ctx != null; ctx = ctx.next)
1936 String textContent = ctx.getTextContent(false);
1937 if (textContent != null)
1939 buffer.append(textContent);
1942 return buffer.toString();
1943 case TEXT_NODE:
1944 case CDATA_SECTION_NODE:
1945 if (((Text) this).isElementContentWhitespace())
1947 return "";
1949 return getNodeValue();
1950 case ATTRIBUTE_NODE:
1951 return getNodeValue();
1952 case COMMENT_NODE:
1953 case PROCESSING_INSTRUCTION_NODE:
1954 return topLevel ? getNodeValue() : "";
1955 default:
1956 return null;
1960 public void setTextContent(String textContent)
1961 throws DOMException
1963 switch (nodeType)
1965 case ELEMENT_NODE:
1966 case ATTRIBUTE_NODE:
1967 case ENTITY_NODE:
1968 case ENTITY_REFERENCE_NODE:
1969 case DOCUMENT_FRAGMENT_NODE:
1970 for (DomNode ctx = first; ctx != null; )
1972 DomNode n = ctx.next;
1973 removeChild(ctx);
1974 ctx = n;
1976 if (textContent != null)
1978 Text text = owner.createTextNode(textContent);
1979 appendChild(text);
1981 break;
1982 case TEXT_NODE:
1983 case CDATA_SECTION_NODE:
1984 case COMMENT_NODE:
1985 case PROCESSING_INSTRUCTION_NODE:
1986 setNodeValue(textContent);
1987 break;
1991 public boolean isSameNode(Node other)
1993 return this == other;
1996 public String lookupPrefix(String namespaceURI)
1998 return (parent == null || parent == owner) ? null :
1999 parent.lookupPrefix(namespaceURI);
2002 public boolean isDefaultNamespace(String namespaceURI)
2004 return (parent == null || parent == owner) ? false :
2005 parent.isDefaultNamespace(namespaceURI);
2008 public String lookupNamespaceURI(String prefix)
2010 return (parent == null || parent == owner) ? null :
2011 parent.lookupNamespaceURI(prefix);
2014 public boolean isEqualNode(Node arg)
2016 if (this == arg)
2018 return true;
2020 if (arg == null)
2022 return false;
2024 if (nodeType != arg.getNodeType() ||
2025 !equal(getNodeName(), arg.getNodeName()) ||
2026 !equal(getLocalName(), arg.getLocalName()) ||
2027 !equal(getNamespaceURI(), arg.getNamespaceURI()) ||
2028 !equal(getPrefix(), arg.getPrefix()) ||
2029 !equal(getNodeValue(), arg.getNodeValue()))
2031 return false;
2033 // Children
2034 Node argCtx = arg.getFirstChild();
2035 getFirstChild(); // because of DomAttr lazy children
2036 for (DomNode ctx = first; ctx != null; ctx = ctx.next)
2038 if (!ctx.isEqualNode(argCtx))
2040 return false;
2042 argCtx = argCtx.getNextSibling();
2044 if (argCtx != null)
2046 return false;
2049 // TODO Attr NamedNodeMap
2050 // TODO DocumentType
2051 return true;
2054 boolean equal(String arg1, String arg2)
2056 return ((arg1 == null && arg2 == null) ||
2057 (arg1 != null && arg1.equals(arg2)));
2060 public Object getFeature(String feature, String version)
2062 DOMImplementation impl = (nodeType == DOCUMENT_NODE) ?
2063 ((Document) this).getImplementation() : owner.getImplementation();
2064 if (impl.hasFeature(feature, version))
2066 return this;
2068 return null;
2071 public Object setUserData(String key, Object data, UserDataHandler handler)
2073 if (userData == null)
2075 userData = new HashMap();
2077 if (handler != null)
2079 if (userDataHandlers == null)
2081 userDataHandlers = new HashMap();
2083 userDataHandlers.put(key, handler);
2085 return userData.put(key, data);
2088 public Object getUserData(String key)
2090 if (userData == null)
2092 return null;
2094 return userData.get(key);
2097 public String toString()
2099 String nodeName = getNodeName();
2100 String nodeValue = getNodeValue();
2101 StringBuffer buf = new StringBuffer(getClass().getName());
2102 buf.append('[');
2103 if (nodeName != null)
2105 buf.append(nodeName);
2107 if (nodeValue != null)
2109 if (nodeName != null)
2111 buf.append('=');
2113 buf.append('\'');
2114 buf.append(encode(nodeValue));
2115 buf.append('\'');
2117 buf.append(']');
2118 return buf.toString();
2121 String encode(String value)
2123 StringBuffer buf = null;
2124 int len = value.length();
2125 for (int i = 0; i < len; i++)
2127 char c = value.charAt(i);
2128 if (c == '\n')
2130 if (buf == null)
2132 buf = new StringBuffer(value.substring(0, i));
2134 buf.append("\\n");
2136 else if (c == '\r')
2138 if (buf == null)
2140 buf = new StringBuffer(value.substring(0, i));
2142 buf.append("\\r");
2144 else if (buf != null)
2146 buf.append(c);
2149 return (buf != null) ? buf.toString() : value;
2152 String nodeTypeToString(short nodeType)
2154 switch (nodeType)
2156 case ELEMENT_NODE:
2157 return "ELEMENT_NODE";
2158 case ATTRIBUTE_NODE:
2159 return "ATTRIBUTE_NODE";
2160 case TEXT_NODE:
2161 return "TEXT_NODE";
2162 case CDATA_SECTION_NODE:
2163 return "CDATA_SECTION_NODE";
2164 case DOCUMENT_NODE:
2165 return "DOCUMENT_NODE";
2166 case DOCUMENT_TYPE_NODE:
2167 return "DOCUMENT_TYPE_NODE";
2168 case COMMENT_NODE:
2169 return "COMMENT_NODE";
2170 case PROCESSING_INSTRUCTION_NODE:
2171 return "PROCESSING_INSTRUCTION_NODE";
2172 case DOCUMENT_FRAGMENT_NODE:
2173 return "DOCUMENT_FRAGMENT_NODE";
2174 case ENTITY_NODE:
2175 return "ENTITY_NODE";
2176 case ENTITY_REFERENCE_NODE:
2177 return "ENTITY_REFERENCE_NODE";
2178 case NOTATION_NODE:
2179 return "NOTATION_NODE";
2180 default:
2181 return "UNKNOWN";