1 /*******************************************************************************
2 * Copyright (c) 2000, 2003 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package net.sourceforge.phpdt.internal.core.jdom;
13 import java.util.Enumeration;
15 import net.sourceforge.phpdt.core.jdom.DOMException;
16 import net.sourceforge.phpdt.core.jdom.DOMFactory;
17 import net.sourceforge.phpdt.core.jdom.IDOMCompilationUnit;
18 import net.sourceforge.phpdt.core.jdom.IDOMFactory;
19 import net.sourceforge.phpdt.core.jdom.IDOMMethod;
20 import net.sourceforge.phpdt.core.jdom.IDOMNode;
21 import net.sourceforge.phpdt.internal.compiler.util.Util;
22 import net.sourceforge.phpdt.internal.core.util.CharArrayBuffer;
25 * DOMNode provides an implementation for <code>IDOMNode</code>.
27 * <p>A node represents a document fragment. When a node is created, its
28 * contents are located in a contiguous range of a shared document. A shared
29 * document is a char array, and is shared in the sense that the contents of other
30 * document fragments may also be contained in the array.
32 * <p>A node maintains indicies of relevant portions of its contents
33 * in the shared document. Thus the original document and indicies create a
34 * form from which to generate the contents of the document fragment. As attributes
35 * of a node are changed, the node attempts to maintain the original formatting
36 * by only replacing relevant portions of the shared document with the value
37 * of new attributes (that is, filling in the form with replacement values).
39 * <p>When a node is first created, it is considered unfragmented. When any
40 * attribute of the node is altered, the node is then considered fragmented
41 * from that point on. A node is also considered fragmented if any of its
42 * descendants are fragmented. When a node is unfragmented, the contents of the
43 * node can be efficiently generated from the original shared document. When
44 * a node is fragmented, the contents of the node must be created using the
45 * original document and indicies as a form, filling in replacement values
48 * <p>Generally, a node's contents consists of complete lines in a shared document.
49 * The contents of the node are normalized on creation to include any whitespace
50 * preceding the node on the line where the node begins, and to include and trailing
51 * whitespace up to the line where the next node begins. Any trailing // comments
52 * that begin on the line where the current node ends, are considered part of that
57 public abstract class DOMNode implements IDOMNode {
60 * The first child of this node - <code>null</code>
61 * when this node has no children. (Children of a node
62 * are implemented as a doubly linked list).
64 protected DOMNode fFirstChild= null;
67 * The last child of this node - <code>null</code>
68 * when this node has no children. Used for efficient
69 * access to the last child when adding new children
70 * at the end of the linked list of children.
72 protected DOMNode fLastChild= null;
75 * The sibling node following this node - <code>null</code>
76 * for the last node in the sibling list.
78 protected DOMNode fNextNode= null;
81 * The parent of this node. A <code>null</code>
82 * parent indicates that this node is a root
83 * node of a document fragment.
85 protected DOMNode fParent= null;
88 * The sibling node preceding this node - <code>null</code>
89 * for the first node in the sibling list.
91 protected DOMNode fPreviousNode= null;
94 * True when this node has attributes that have
95 * been altered from their original state in the
96 * shared document, or when the attributes of a
97 * descendant have been altered. False when the
98 * contents of this node and all descendants are
99 * consistent with the content of the shared
102 protected boolean fIsFragmented= false;
105 * The name of this node. For efficiency, the
106 * name of a node is duplicated in this variable
107 * on creation, rather than always having to fetch
108 * the name from the shared document.
110 protected String fName= null;
113 * The original inclusive indicies of this node's name in
114 * the shared document. Values of -1 indiciate the name
115 * does not exist in the document.
117 protected int[] fNameRange;
120 * The shared document that the contents for this node
121 * are contained in. Attribute indicies are positions
122 * in this character array.
124 protected char[] fDocument= null;
127 * The original entire inclusive range of this node's contents
128 * within its document. Values of -1 indicate the contents
129 * of this node do not exist in the document.
131 protected int[] fSourceRange;
134 * The current state of bit masks defined by this node.
135 * Initially all bit flags are turned off. All bit masks
136 * are defined by this class to avoid overlap, although
137 * bit masks are node type specific.
142 protected int fStateMask= 0;
145 * This position is the position of the end of the last line separator before the closing brace starting
146 * position of the receiver.
148 protected int fInsertionPosition;
151 * A bit mask indicating this field has an initializer
154 protected static final int MASK_FIELD_HAS_INITIALIZER= 0x00000001;
157 * A bit mask indicating this field is a secondary variable
158 * declarator for a previous field declaration.
160 protected static final int MASK_FIELD_IS_VARIABLE_DECLARATOR= 0x00000002;
163 * A bit mask indicating this field's type has been
164 * altered from its original contents in the document.
166 protected static final int MASK_FIELD_TYPE_ALTERED= 0x00000004;
169 * A bit mask indicating this node's name has been
170 * altered from its original contents in the document.
172 protected static final int MASK_NAME_ALTERED= 0x00000008;
175 * A bit mask indicating this node currently has a
178 protected static final int MASK_HAS_BODY= 0x00000010;
181 * A bit mask indicating this node currently has a
184 protected static final int MASK_HAS_COMMENT= 0x00000020;
187 * A bit mask indicating this method is a constructor.
189 protected static final int MASK_IS_CONSTRUCTOR= 0x00000040;
192 * A bit mask indicating this type is a class.
194 protected static final int MASK_TYPE_IS_CLASS= 0x00000080;
197 * A bit mask indicating this type has a superclass
198 * (requires or has an 'extends' clause).
200 protected static final int MASK_TYPE_HAS_SUPERCLASS= 0x00000100;
203 * A bit mask indicating this type implements
204 * or extends some interfaces
206 protected static final int MASK_TYPE_HAS_INTERFACES= 0x00000200;
209 * A bit mask indicating this return type of this method has
210 * been altered from the original contents.
212 protected static final int MASK_RETURN_TYPE_ALTERED= 0x00000400;
215 * A bit mask indicating this node has detailed source indexes
217 protected static final int MASK_DETAILED_SOURCE_INDEXES = 0x00000800;
220 * Creates a new empty document fragment.
225 fSourceRange= new int[]{-1, -1};
226 fNameRange= new int[]{-1, -1};
230 * Creates a new document fragment on the given range of the document.
232 * @param document - the document containing this node's original contents
233 * @param sourceRange - a two element array of integers describing the
234 * entire inclusive source range of this node within its document.
235 * Contents start on and include the character at the first position.
236 * Contents end on and include the character at the last position.
237 * An array of -1's indicates this node's contents do not exist
239 * @param name - the identifier portion of the name of this node, or
240 * <code>null</code> if this node does not have a name
241 * @param nameRange - a two element array of integers describing the
242 * entire inclusive source range of this node's name within its document,
243 * including any array qualifiers that might immediately follow the name
244 * or -1's if this node does not have a name.
246 DOMNode(char[] document, int[] sourceRange, String name, int[] nameRange) {
249 fSourceRange= sourceRange;
251 fNameRange= nameRange;
255 * Adds the given un-parented node (document fragment) as the last child of
258 * <p>When a child is added, this node must be considered fragmented such that
259 * the contents of this node are properly generated.
261 * @see IDOMNode#addChild(IDOMNode)
263 public void addChild(IDOMNode child) throws IllegalArgumentException, DOMException {
264 basicAddChild(child);
266 // if the node is a constructor, it must also be fragmented to update the constructor's name
267 if (child.getNodeType() == IDOMNode.METHOD && ((IDOMMethod)child).isConstructor()) {
268 ((DOMNode)child).fragment();
274 * Appends the current contents of this document fragment
275 * to the given <code>CharArrayBuffer</code>.
277 * <p>If this node is fragmented, contents must be generated by
278 * using the original document and indicies as a form for the current
279 * attribute values of this node. If this node not fragmented, the
280 * contents can be obtained from the document.
283 protected void appendContents(CharArrayBuffer buffer) {
284 if (isFragmented()) {
285 appendFragmentedContents(buffer);
287 buffer.append(fDocument, fSourceRange[0], fSourceRange[1] + 1 - fSourceRange[0]);
291 * Appends the contents of all children of this node to the
292 * given <code>CharArrayBuffer</code>.
294 * <p>This algorithm used minimizes String generation by merging
295 * adjacent unfragmented children into one substring operation.
298 protected void appendContentsOfChildren(CharArrayBuffer buffer) {
299 DOMNode child= fFirstChild;
302 int start= 0, end= 0;
304 start= child.getStartPosition();
305 end= child.getEndPosition();
307 while (child != null) {
308 sibling= child.fNextNode;
309 if (sibling != null) {
310 if (sibling.isContentMergableWith(child)) {
311 end= sibling.getEndPosition();
313 if (child.isFragmented()) {
314 child.appendContents(buffer);
316 buffer.append(child.getDocument(), start, end + 1 - start);
318 start= sibling.getStartPosition();
319 end= sibling.getEndPosition();
322 if (child.isFragmented()) {
323 child.appendContents(buffer);
325 buffer.append(child.getDocument(), start, end + 1 - start);
332 * Appends the contents of this node to the given <code>CharArrayBufer</code>, using
333 * the original document and indicies as a form for the current attribute
334 * values of this node.
336 protected abstract void appendFragmentedContents(CharArrayBuffer buffer);
338 * Adds the given un-parented node (document fragment) as the last child of
339 * this node without setting this node's 'fragmented' flag. This
340 * method is only used by the <code>DOMBuilder</code> when creating a new DOM such
341 * that a new DOM is unfragmented.
343 void basicAddChild(IDOMNode child) throws IllegalArgumentException, DOMException {
344 // verify child may be added
345 if (!canHaveChildren()) {
346 throw new DOMException(Util.bind("dom.unableAddChild")); //$NON-NLS-1$
349 throw new IllegalArgumentException(Util.bind("dom.addNullChild")); //$NON-NLS-1$
351 if (!isAllowableChild(child)) {
352 throw new DOMException(Util.bind("dom.addIncompatibleChild")); //$NON-NLS-1$
354 if (child.getParent() != null) {
355 throw new DOMException(Util.bind("dom.addChildWithParent")); //$NON-NLS-1$
357 /* NOTE: To test if the child is an ancestor of this node, we
358 * need only test if the root of this node is the child (the child
359 * is already a root since we have just guarenteed it has no parent).
361 if (child == getRoot()) {
362 throw new DOMException(Util.bind("dom.addAncestorAsChild")); //$NON-NLS-1$
365 DOMNode node= (DOMNode)child;
367 // if the child is not already part of this document, localize its contents
368 // before adding it to the tree
369 if (node.getDocument() != getDocument()) {
370 node.localizeContents();
373 // add the child last
374 if (fFirstChild == null) {
375 // this is the first and only child
378 fLastChild.fNextNode= node;
379 node.fPreviousNode= fLastChild;
385 * Generates detailed source indexes for this node if possible.
387 * @exception DOMException if unable to generate detailed source indexes
390 protected void becomeDetailed() throws DOMException {
392 DOMNode detailed= getDetailedNode();
393 if (detailed == null) {
394 throw new DOMException(Util.bind("dom.cannotDetail")); //$NON-NLS-1$
396 if (detailed != this) {
397 shareContents(detailed);
402 * Returns true if this node is allowed to have children, otherwise false.
404 * <p>Default implementation of <code>IDOMNode</code> interface method returns false; this
405 * method must be overridden by subclasses that implement nodes that allow
408 * @see IDOMNode#canHaveChildren()
410 public boolean canHaveChildren() {
414 * @see IDOMNode#clone()
416 public Object clone() {
418 // create a new buffer with all my contents and children contents
421 int offset= fSourceRange[0];
424 length= fSourceRange[1] - offset + 1;
425 buffer= new char[length];
426 System.arraycopy(fDocument, offset, buffer, 0, length);
428 DOMNode clone= newDOMNode();
429 clone.shareContents(this);
430 clone.fDocument = buffer;
433 clone.offset(0 - offset);
437 if (canHaveChildren()) {
438 Enumeration children= getChildren();
439 while (children.hasMoreElements()) {
440 DOMNode child= (DOMNode)children.nextElement();
441 if (child.fDocument == fDocument) {
442 DOMNode childClone= child.cloneSharingDocument(buffer, offset);
443 clone.basicAddChild(childClone);
445 DOMNode childClone= (DOMNode)child.clone();
446 clone.addChild(childClone);
454 private DOMNode cloneSharingDocument(char[] document, int rootOffset) {
456 DOMNode clone = newDOMNode();
457 clone.shareContents(this);
458 clone.fDocument = document;
459 if (rootOffset > 0) {
460 clone.offset(0 - rootOffset);
463 if (canHaveChildren()) {
464 Enumeration children = getChildren();
465 while (children.hasMoreElements()) {
466 DOMNode child = (DOMNode) children.nextElement();
467 if (child.fDocument == fDocument) {
468 DOMNode childClone= child.cloneSharingDocument(document, rootOffset);
469 clone.basicAddChild(childClone);
471 DOMNode childClone= (DOMNode)child.clone();
472 clone.addChild(childClone);
479 * Sets this node's fragmented flag and all ancestor fragmented flags
480 * to <code>true<code>. This happens when an attribute of this node or a descendant
481 * node has been altered. When a node is fragmented, its contents must
482 * be generated from its attributes and original "form" rather than
483 * from the original contents in the document.
485 protected void fragment() {
486 if (!isFragmented()) {
488 if (fParent != null) {
494 * @see IDOMNode#getCharacters()
496 public char[] getCharacters() {
497 CharArrayBuffer buffer= new CharArrayBuffer();
498 appendContents(buffer);
499 return buffer.getContents();
502 * @see IDOMNode#getChild(String)
504 public IDOMNode getChild(String name) {
505 DOMNode child = fFirstChild;
506 while (child != null) {
507 String n = child.getName();
513 if (name.equals(n)) {
517 child = child.fNextNode;
522 * @see IDOMNode#getChildren()
524 public Enumeration getChildren() {
525 return new SiblingEnumeration(fFirstChild);
528 * Returns the current contents of this document fragment,
529 * or <code>null</code> if this node has no contents.
531 * <p>If this node is fragmented, contents must be generated by
532 * using the original document and indicies as a form for the current
533 * attribute values of this node. If this node not fragmented, the
534 * contents can be obtained from the document.
536 * @see IDOMNode#getContents()
538 public String getContents() {
539 CharArrayBuffer buffer= new CharArrayBuffer();
540 appendContents(buffer);
541 return buffer.toString();
544 * Returns a new document fragment representing this node with
545 * detailed source indexes. Subclasses that provide a detailed
546 * implementation must override this method.
548 protected DOMNode getDetailedNode() {
552 * Returns the document containing this node's original contents.
553 * The document may be shared by other nodes.
555 protected char[] getDocument() {
559 * Returns the original position of the last character of this
560 * node's contents in its document.
562 public int getEndPosition() {
563 return fSourceRange[1];
566 * Returns a factory with which to create new document fragments.
568 protected IDOMFactory getFactory() {
569 return new DOMFactory();
572 * @see IDOMNode#getFirstChild()
574 public IDOMNode getFirstChild() {
578 * Returns the position at which the first child of this node should be inserted.
580 public int getInsertionPosition() {
581 return fInsertionPosition;
584 * Returns <code>true</code> if the given mask of this node's state flag
585 * is turned on, otherwise <code>false</code>.
587 protected boolean getMask(int mask) {
588 return (fStateMask & mask) > 0;
591 * @see IDOMNode#getName()
593 public String getName() {
597 * Returns the source code to be used for this node's name.
599 protected char[] getNameContents() {
600 if (isNameAltered()) {
601 return fName.toCharArray();
603 if (fName == null || fNameRange[0] < 0) {
606 int length = fNameRange[1] + 1 - fNameRange[0];
607 char[] result = new char[length];
608 System.arraycopy(fDocument, fNameRange[0], result, 0, length);
614 * @see IDOMNode#getNextNode()
616 public IDOMNode getNextNode() {
620 * @see IDOMNode#getParent()
622 public IDOMNode getParent() {
626 * Answers a source position which corresponds to the end of the parent
627 * element's declaration.
629 protected int getParentEndDeclaration() {
630 IDOMNode parent = getParent();
631 if (parent == null) {
634 if (parent instanceof IDOMCompilationUnit) {
637 return ((DOMType)parent).getOpenBodyEnd();
642 * @see IDOMNode#getPreviousNode()
644 public IDOMNode getPreviousNode() {
645 return fPreviousNode;
648 * Returns the root node of this document fragment.
650 protected IDOMNode getRoot() {
651 if (fParent == null) {
654 return fParent.getRoot();
658 * Returns the original position of the first character of this
659 * node's contents in its document.
661 public int getStartPosition() {
662 return fSourceRange[0];
665 * @see IDOMNode#insertSibling(IDOMNode)
667 public void insertSibling(IDOMNode sibling) throws IllegalArgumentException, DOMException {
668 // verify sibling may be added
669 if (sibling == null) {
670 throw new IllegalArgumentException(Util.bind("dom.addNullSibling")); //$NON-NLS-1$
672 if (fParent == null) {
673 throw new DOMException(Util.bind("dom.addSiblingBeforeRoot")); //$NON-NLS-1$
675 if (!fParent.isAllowableChild(sibling)) {
676 throw new DOMException(Util.bind("dom.addIncompatibleSibling")); //$NON-NLS-1$
678 if (sibling.getParent() != null) {
679 throw new DOMException(Util.bind("dom.addSiblingWithParent")); //$NON-NLS-1$
681 /* NOTE: To test if the sibling is an ancestor of this node, we
682 * need only test if the root of this node is the child (the sibling
683 * is already a root since we have just guaranteed it has no parent).
685 if (sibling == getRoot()) {
686 throw new DOMException(Util.bind("dom.addAncestorAsSibling")); //$NON-NLS-1$
689 DOMNode node= (DOMNode)sibling;
691 // if the sibling is not already part of this document, localize its contents
692 // before inserting it into the tree
693 if (node.getDocument() != getDocument()) {
694 node.localizeContents();
698 if (fPreviousNode == null) {
699 fParent.fFirstChild= node;
701 fPreviousNode.fNextNode= node;
703 node.fParent= fParent;
704 node.fPreviousNode= fPreviousNode;
705 node.fNextNode= this;
708 // if the node is a constructor, it must also be fragmented to update the constructor's name
709 if (node.getNodeType() == IDOMNode.METHOD && ((IDOMMethod)node).isConstructor()) {
718 public boolean isAllowableChild(IDOMNode node) {
722 * Returns <code>true</code> if the contents of this node are from the same document as
723 * the given node, the contents of this node immediately follow the contents
724 * of the given node, and neither this node or the given node are fragmented -
725 * otherwise <code>false</code>.
727 protected boolean isContentMergableWith(DOMNode node) {
728 return !node.isFragmented() && !isFragmented() && node.getDocument() == getDocument() &&
729 node.getEndPosition() + 1 == getStartPosition();
732 * Returns <code>true</code> if this node has detailed source index information,
733 * or <code>false</code> if this node has limited source index information. To
734 * perform some manipulations, detailed indexes are required.
736 protected boolean isDetailed() {
737 return getMask(MASK_DETAILED_SOURCE_INDEXES);
740 * Returns <code>true</code> if this node's or a descendant node's contents
741 * have been altered since this node was created. This indicates
742 * that the contents of this node are no longer consistent with
743 * the contents of this node's document.
745 protected boolean isFragmented() {
746 return fIsFragmented;
749 * Returns <code>true</code> if this noed's name has been altered
750 * from the original document contents.
752 protected boolean isNameAltered() {
753 return getMask(MASK_NAME_ALTERED);
756 * @see IDOMNode#isSignatureEqual(IDOMNode).
758 * <p>By default, the signatures of two nodes are equal if their
759 * type and names are equal. Node types that have other requirements
760 * for equality must override this method.
762 public boolean isSignatureEqual(IDOMNode node) {
763 return getNodeType() == node.getNodeType() && getName().equals(node.getName());
766 * Localizes the contents of this node and all descendant nodes,
767 * such that this node is no longer dependent on its original
768 * document in order to generate its contents. This node and all
769 * descendant nodes become unfragmented and share a new
772 protected void localizeContents() {
774 DOMNode clone= (DOMNode)clone();
775 shareContents(clone);
779 * Returns a new empty <code>DOMNode</code> for this instance.
781 protected abstract DOMNode newDOMNode();
783 * Normalizes this <code>DOMNode</code>'s source positions to include whitespace preceeding
784 * the node on the line on which the node starts, and all whitespace after the node up to
785 * the next node's start
787 void normalize(ILineStartFinder finder) {
788 if (getPreviousNode() == null)
789 normalizeStartPosition(getParentEndDeclaration(), finder);
791 // Set the children's position
792 if (canHaveChildren()) {
793 Enumeration children = getChildren();
794 while(children.hasMoreElements())
795 ((DOMNode)children.nextElement()).normalize(finder);
798 normalizeEndPosition(finder, (DOMNode)getNextNode());
801 * Normalizes this <code>DOMNode</code>'s end position.
803 void normalizeEndPosition(ILineStartFinder finder, DOMNode next) {
805 // this node's end position includes all of the characters up
806 // to the end of the enclosing node
807 DOMNode parent = (DOMNode) getParent();
808 if (parent == null || parent instanceof DOMCompilationUnit) {
809 setSourceRangeEnd(fDocument.length - 1);
812 int temp = ((DOMType)parent).getCloseBodyPosition() - 1;
813 setSourceRangeEnd(temp);
814 fInsertionPosition = Math.max(finder.getLineStart(temp + 1), getEndPosition());
817 // this node's end position is just before the start of the next node
818 int temp = next.getStartPosition() - 1;
819 fInsertionPosition = Math.max(finder.getLineStart(temp + 1), getEndPosition());
820 next.normalizeStartPosition(getEndPosition(), finder);
821 setSourceRangeEnd(next.getStartPosition() - 1);
825 * Normalizes this <code>DOMNode</code>'s start position.
827 void normalizeStartPosition(int previousEnd, ILineStartFinder finder) {
828 int nodeStart = getStartPosition();
829 int lineStart = finder.getLineStart(nodeStart);
830 if (nodeStart > lineStart && (lineStart > previousEnd || (previousEnd == 0 && lineStart == 0)))
831 setStartPosition(lineStart);
834 * Offsets all the source indexes in this node by the given amount.
836 protected void offset(int offset) {
837 offsetRange(fNameRange, offset);
838 offsetRange(fSourceRange, offset);
841 * Offsets the source range by the given amount
843 protected void offsetRange(int[] range, int offset) {
844 for (int i= 0; i < range.length; i++) {
852 * Returns a copy of the given range.
854 protected int[] rangeCopy(int[] range) {
855 int[] copy= new int[range.length];
856 for (int i= 0; i < range.length; i++) {
862 * Separates this node from its parent and siblings, maintaining any ties that
863 * this node has to the underlying document fragment.
865 * <p>When a child is removed, its parent is fragmented such that it properly
866 * generates its contents.
868 * @see IDOMNode#remove()
870 public void remove() {
872 if (fParent != null) {
877 if (fNextNode != null) {
878 fNextNode.fPreviousNode= fPreviousNode;
880 if (fPreviousNode != null) {
881 fPreviousNode.fNextNode= fNextNode;
883 // fix parent's pointers
884 if (fParent != null) {
885 if (fParent.fFirstChild == this) {
886 fParent.fFirstChild= fNextNode;
888 if (fParent.fLastChild == this) {
889 fParent.fLastChild= fPreviousNode;
898 * Sets the specified mask of this node's state mask on or off
899 * based on the boolean value - true -> on, false -> off.
901 protected void setMask(int mask, boolean on) {
909 * @see IDOMNode#setName
911 public void setName(String name) {
913 setNameAltered(true);
917 * Sets the state of this node as having
918 * its name attribute altered from the original
921 protected void setNameAltered(boolean altered) {
922 setMask(MASK_NAME_ALTERED, altered);
925 * Sets the original position of the last character of this node's contents
926 * in its document. This method is only used during DOM creation while
927 * normalizing the source range of each node.
929 protected void setSourceRangeEnd(int end) {
930 fSourceRange[1]= end;
933 * Sets the original position of the first character of this node's contents
934 * in its document. This method is only used during DOM creation while
935 * normalizing the source range of each node.
937 protected void setStartPosition(int start) {
938 fSourceRange[0]= start;
941 * Sets the contents of this node and descendant nodes to be the
942 * (identical) contents of the given node and its descendants. This
943 * does not effect this node's parent and sibling configuration,
944 * only the contents of this node. This is used only to localize
945 * the contents of this node.
947 protected void shareContents(DOMNode node) {
948 fDocument= node.fDocument;
949 fIsFragmented= node.fIsFragmented;
951 fNameRange= rangeCopy(node.fNameRange);
952 fSourceRange= rangeCopy(node.fSourceRange);
953 fStateMask= node.fStateMask;
956 if (canHaveChildren()) {
957 Enumeration myChildren= getChildren();
958 Enumeration otherChildren= node.getChildren();
959 DOMNode myChild, otherChild;
960 while (myChildren.hasMoreElements()) {
961 myChild= (DOMNode)myChildren.nextElement();
962 otherChild= (DOMNode)otherChildren.nextElement();
963 myChild.shareContents(otherChild);
968 * Returns a <code>String</code> representing this node - for Debug purposes only.
970 public abstract String toString();