A massive organize imports and formatting of the sources using default Eclipse code...
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / jdom / DOMNode.java
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
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.core.jdom;
12
13 import java.util.Enumeration;
14
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;
23
24 /**
25  * DOMNode provides an implementation for <code>IDOMNode</code>.
26  * 
27  * <p>
28  * A node represents a document fragment. When a node is created, its contents
29  * are located in a contiguous range of a shared document. A shared document is
30  * a char array, and is shared in the sense that the contents of other document
31  * fragments may also be contained in the array.
32  * 
33  * <p>
34  * A node maintains indicies of relevant portions of its contents in the shared
35  * document. Thus the original document and indicies create a form from which to
36  * generate the contents of the document fragment. As attributes of a node are
37  * changed, the node attempts to maintain the original formatting by only
38  * replacing relevant portions of the shared document with the value of new
39  * attributes (that is, filling in the form with replacement values).
40  * 
41  * <p>
42  * When a node is first created, it is considered unfragmented. When any
43  * attribute of the node is altered, the node is then considered fragmented from
44  * that point on. A node is also considered fragmented if any of its descendants
45  * are fragmented. When a node is unfragmented, the contents of the node can be
46  * efficiently generated from the original shared document. When a node is
47  * fragmented, the contents of the node must be created using the original
48  * document and indicies as a form, filling in replacement values as required.
49  * 
50  * <p>
51  * Generally, a node's contents consists of complete lines in a shared document.
52  * The contents of the node are normalized on creation to include any whitespace
53  * preceding the node on the line where the node begins, and to include and
54  * trailing whitespace up to the line where the next node begins. Any trailing //
55  * comments that begin on the line where the current node ends, are considered
56  * part of that node.
57  * 
58  * @see IDOMNode
59  */
60 public abstract class DOMNode implements IDOMNode {
61
62         /**
63          * The first child of this node - <code>null</code> when this node has no
64          * children. (Children of a node are implemented as a doubly linked list).
65          */
66         protected DOMNode fFirstChild = null;
67
68         /**
69          * The last child of this node - <code>null</code> when this node has no
70          * children. Used for efficient access to the last child when adding new
71          * children at the end of the linked list of children.
72          */
73         protected DOMNode fLastChild = null;
74
75         /**
76          * The sibling node following this node - <code>null</code> for the last
77          * node in the sibling list.
78          */
79         protected DOMNode fNextNode = null;
80
81         /**
82          * The parent of this node. A <code>null</code> parent indicates that this
83          * node is a root node of a document fragment.
84          */
85         protected DOMNode fParent = null;
86
87         /**
88          * The sibling node preceding this node - <code>null</code> for the first
89          * node in the sibling list.
90          */
91         protected DOMNode fPreviousNode = null;
92
93         /**
94          * True when this node has attributes that have been altered from their
95          * original state in the shared document, or when the attributes of a
96          * descendant have been altered. False when the contents of this node and
97          * all descendants are consistent with the content of the shared document.
98          */
99         protected boolean fIsFragmented = false;
100
101         /**
102          * The name of this node. For efficiency, the name of a node is duplicated
103          * in this variable on creation, rather than always having to fetch the name
104          * from the shared document.
105          */
106         protected String fName = null;
107
108         /**
109          * The original inclusive indicies of this node's name in the shared
110          * document. Values of -1 indiciate the name does not exist in the document.
111          */
112         protected int[] fNameRange;
113
114         /**
115          * The shared document that the contents for this node are contained in.
116          * Attribute indicies are positions in this character array.
117          */
118         protected char[] fDocument = null;
119
120         /**
121          * The original entire inclusive range of this node's contents within its
122          * document. Values of -1 indicate the contents of this node do not exist in
123          * the document.
124          */
125         protected int[] fSourceRange;
126
127         /**
128          * The current state of bit masks defined by this node. Initially all bit
129          * flags are turned off. All bit masks are defined by this class to avoid
130          * overlap, although bit masks are node type specific.
131          * 
132          * @see #setMask
133          * @see #getMask
134          */
135         protected int fStateMask = 0;
136
137         /**
138          * This position is the position of the end of the last line separator
139          * before the closing brace starting position of the receiver.
140          */
141         protected int fInsertionPosition;
142
143         /**
144          * A bit mask indicating this field has an initializer expression
145          */
146         protected static final int MASK_FIELD_HAS_INITIALIZER = 0x00000001;
147
148         /**
149          * A bit mask indicating this field is a secondary variable declarator for a
150          * previous field declaration.
151          */
152         protected static final int MASK_FIELD_IS_VARIABLE_DECLARATOR = 0x00000002;
153
154         /**
155          * A bit mask indicating this field's type has been altered from its
156          * original contents in the document.
157          */
158         protected static final int MASK_FIELD_TYPE_ALTERED = 0x00000004;
159
160         /**
161          * A bit mask indicating this node's name has been altered from its original
162          * contents in the document.
163          */
164         protected static final int MASK_NAME_ALTERED = 0x00000008;
165
166         /**
167          * A bit mask indicating this node currently has a body.
168          */
169         protected static final int MASK_HAS_BODY = 0x00000010;
170
171         /**
172          * A bit mask indicating this node currently has a preceding comment.
173          */
174         protected static final int MASK_HAS_COMMENT = 0x00000020;
175
176         /**
177          * A bit mask indicating this method is a constructor.
178          */
179         protected static final int MASK_IS_CONSTRUCTOR = 0x00000040;
180
181         /**
182          * A bit mask indicating this type is a class.
183          */
184         protected static final int MASK_TYPE_IS_CLASS = 0x00000080;
185
186         /**
187          * A bit mask indicating this type has a superclass (requires or has an
188          * 'extends' clause).
189          */
190         protected static final int MASK_TYPE_HAS_SUPERCLASS = 0x00000100;
191
192         /**
193          * A bit mask indicating this type implements or extends some interfaces
194          */
195         protected static final int MASK_TYPE_HAS_INTERFACES = 0x00000200;
196
197         /**
198          * A bit mask indicating this return type of this method has been altered
199          * from the original contents.
200          */
201         protected static final int MASK_RETURN_TYPE_ALTERED = 0x00000400;
202
203         /**
204          * A bit mask indicating this node has detailed source indexes
205          */
206         protected static final int MASK_DETAILED_SOURCE_INDEXES = 0x00000800;
207
208         /**
209          * Creates a new empty document fragment.
210          */
211         DOMNode() {
212                 fName = null;
213                 fDocument = null;
214                 fSourceRange = new int[] { -1, -1 };
215                 fNameRange = new int[] { -1, -1 };
216                 fragment();
217         }
218
219         /**
220          * Creates a new document fragment on the given range of the document.
221          * 
222          * @param document -
223          *            the document containing this node's original contents
224          * @param sourceRange -
225          *            a two element array of integers describing the entire
226          *            inclusive source range of this node within its document.
227          *            Contents start on and include the character at the first
228          *            position. Contents end on and include the character at the
229          *            last position. An array of -1's indicates this node's contents
230          *            do not exist in the document.
231          * @param name -
232          *            the identifier portion of the name of this node, or
233          *            <code>null</code> if this node does not have a name
234          * @param nameRange -
235          *            a two element array of integers describing the entire
236          *            inclusive source range of this node's name within its
237          *            document, including any array qualifiers that might
238          *            immediately follow the name or -1's if this node does not have
239          *            a name.
240          */
241         DOMNode(char[] document, int[] sourceRange, String name, int[] nameRange) {
242                 super();
243                 fDocument = document;
244                 fSourceRange = sourceRange;
245                 fName = name;
246                 fNameRange = nameRange;
247
248         }
249
250         /**
251          * Adds the given un-parented node (document fragment) as the last child of
252          * this node.
253          * 
254          * <p>
255          * When a child is added, this node must be considered fragmented such that
256          * the contents of this node are properly generated.
257          * 
258          * @see IDOMNode#addChild(IDOMNode)
259          */
260         public void addChild(IDOMNode child) throws IllegalArgumentException,
261                         DOMException {
262                 basicAddChild(child);
263
264                 // if the node is a constructor, it must also be fragmented to update
265                 // the constructor's name
266                 if (child.getNodeType() == IDOMNode.METHOD
267                                 && ((IDOMMethod) child).isConstructor()) {
268                         ((DOMNode) child).fragment();
269                 } else {
270                         fragment();
271                 }
272         }
273
274         /**
275          * Appends the current contents of this document fragment to the given
276          * <code>CharArrayBuffer</code>.
277          * 
278          * <p>
279          * If this node is fragmented, contents must be generated by using the
280          * original document and indicies as a form for the current attribute values
281          * of this node. If this node not fragmented, the contents can be obtained
282          * from the document.
283          * 
284          */
285         protected void appendContents(CharArrayBuffer buffer) {
286                 if (isFragmented()) {
287                         appendFragmentedContents(buffer);
288                 } else {
289                         buffer.append(fDocument, fSourceRange[0], fSourceRange[1] + 1
290                                         - fSourceRange[0]);
291                 }
292         }
293
294         /**
295          * Appends the contents of all children of this node to the given
296          * <code>CharArrayBuffer</code>.
297          * 
298          * <p>
299          * This algorithm used minimizes String generation by merging adjacent
300          * unfragmented children into one substring operation.
301          * 
302          */
303         protected void appendContentsOfChildren(CharArrayBuffer buffer) {
304                 DOMNode child = fFirstChild;
305                 DOMNode sibling;
306
307                 int start = 0, end = 0;
308                 if (child != null) {
309                         start = child.getStartPosition();
310                         end = child.getEndPosition();
311                 }
312                 while (child != null) {
313                         sibling = child.fNextNode;
314                         if (sibling != null) {
315                                 if (sibling.isContentMergableWith(child)) {
316                                         end = sibling.getEndPosition();
317                                 } else {
318                                         if (child.isFragmented()) {
319                                                 child.appendContents(buffer);
320                                         } else {
321                                                 buffer.append(child.getDocument(), start, end + 1
322                                                                 - start);
323                                         }
324                                         start = sibling.getStartPosition();
325                                         end = sibling.getEndPosition();
326                                 }
327                         } else {
328                                 if (child.isFragmented()) {
329                                         child.appendContents(buffer);
330                                 } else {
331                                         buffer.append(child.getDocument(), start, end + 1 - start);
332                                 }
333                         }
334                         child = sibling;
335                 }
336         }
337
338         /**
339          * Appends the contents of this node to the given
340          * <code>CharArrayBufer</code>, using the original document and indicies
341          * as a form for the current attribute values of this node.
342          */
343         protected abstract void appendFragmentedContents(CharArrayBuffer buffer);
344
345         /**
346          * Adds the given un-parented node (document fragment) as the last child of
347          * this node without setting this node's 'fragmented' flag. This method is
348          * only used by the <code>DOMBuilder</code> when creating a new DOM such
349          * that a new DOM is unfragmented.
350          */
351         void basicAddChild(IDOMNode child) throws IllegalArgumentException,
352                         DOMException {
353                 // verify child may be added
354                 if (!canHaveChildren()) {
355                         throw new DOMException(Util.bind("dom.unableAddChild")); //$NON-NLS-1$
356                 }
357                 if (child == null) {
358                         throw new IllegalArgumentException(Util.bind("dom.addNullChild")); //$NON-NLS-1$
359                 }
360                 if (!isAllowableChild(child)) {
361                         throw new DOMException(Util.bind("dom.addIncompatibleChild")); //$NON-NLS-1$
362                 }
363                 if (child.getParent() != null) {
364                         throw new DOMException(Util.bind("dom.addChildWithParent")); //$NON-NLS-1$
365                 }
366                 /*
367                  * NOTE: To test if the child is an ancestor of this node, we need only
368                  * test if the root of this node is the child (the child is already a
369                  * root since we have just guarenteed it has no parent).
370                  */
371                 if (child == getRoot()) {
372                         throw new DOMException(Util.bind("dom.addAncestorAsChild")); //$NON-NLS-1$
373                 }
374
375                 DOMNode node = (DOMNode) child;
376
377                 // if the child is not already part of this document, localize its
378                 // contents
379                 // before adding it to the tree
380                 if (node.getDocument() != getDocument()) {
381                         node.localizeContents();
382                 }
383
384                 // add the child last
385                 if (fFirstChild == null) {
386                         // this is the first and only child
387                         fFirstChild = node;
388                 } else {
389                         fLastChild.fNextNode = node;
390                         node.fPreviousNode = fLastChild;
391                 }
392                 fLastChild = node;
393                 node.fParent = this;
394         }
395
396         /**
397          * Generates detailed source indexes for this node if possible.
398          * 
399          * @exception DOMException
400          *                if unable to generate detailed source indexes for this
401          *                node
402          */
403         protected void becomeDetailed() throws DOMException {
404                 if (!isDetailed()) {
405                         DOMNode detailed = getDetailedNode();
406                         if (detailed == null) {
407                                 throw new DOMException(Util.bind("dom.cannotDetail")); //$NON-NLS-1$
408                         }
409                         if (detailed != this) {
410                                 shareContents(detailed);
411                         }
412                 }
413         }
414
415         /**
416          * Returns true if this node is allowed to have children, otherwise false.
417          * 
418          * <p>
419          * Default implementation of <code>IDOMNode</code> interface method
420          * returns false; this method must be overridden by subclasses that
421          * implement nodes that allow children.
422          * 
423          * @see IDOMNode#canHaveChildren()
424          */
425         public boolean canHaveChildren() {
426                 return false;
427         }
428
429         /**
430          * @see IDOMNode#clone()
431          */
432         public Object clone() {
433
434                 // create a new buffer with all my contents and children contents
435                 int length = 0;
436                 char[] buffer = null;
437                 int offset = fSourceRange[0];
438
439                 if (offset >= 0) {
440                         length = fSourceRange[1] - offset + 1;
441                         buffer = new char[length];
442                         System.arraycopy(fDocument, offset, buffer, 0, length);
443                 }
444                 DOMNode clone = newDOMNode();
445                 clone.shareContents(this);
446                 clone.fDocument = buffer;
447
448                 if (offset > 0) {
449                         clone.offset(0 - offset);
450                 }
451
452                 // clone my children
453                 if (canHaveChildren()) {
454                         Enumeration children = getChildren();
455                         while (children.hasMoreElements()) {
456                                 DOMNode child = (DOMNode) children.nextElement();
457                                 if (child.fDocument == fDocument) {
458                                         DOMNode childClone = child.cloneSharingDocument(buffer,
459                                                         offset);
460                                         clone.basicAddChild(childClone);
461                                 } else {
462                                         DOMNode childClone = (DOMNode) child.clone();
463                                         clone.addChild(childClone);
464                                 }
465
466                         }
467                 }
468
469                 return clone;
470         }
471
472         private DOMNode cloneSharingDocument(char[] document, int rootOffset) {
473
474                 DOMNode clone = newDOMNode();
475                 clone.shareContents(this);
476                 clone.fDocument = document;
477                 if (rootOffset > 0) {
478                         clone.offset(0 - rootOffset);
479                 }
480
481                 if (canHaveChildren()) {
482                         Enumeration children = getChildren();
483                         while (children.hasMoreElements()) {
484                                 DOMNode child = (DOMNode) children.nextElement();
485                                 if (child.fDocument == fDocument) {
486                                         DOMNode childClone = child.cloneSharingDocument(document,
487                                                         rootOffset);
488                                         clone.basicAddChild(childClone);
489                                 } else {
490                                         DOMNode childClone = (DOMNode) child.clone();
491                                         clone.addChild(childClone);
492                                 }
493                         }
494                 }
495                 return clone;
496         }
497
498         /**
499          * Sets this node's fragmented flag and all ancestor fragmented flags to
500          * <code>true<code>. This happens when an attribute of this node or a descendant
501          * node has been altered. When a node is fragmented, its contents must
502          * be generated from its attributes and original "form" rather than
503          * from the original contents in the document.
504          */
505         protected void fragment() {
506                 if (!isFragmented()) {
507                         fIsFragmented = true;
508                         if (fParent != null) {
509                                 fParent.fragment();
510                         }
511                 }
512         }
513
514         /**
515          * @see IDOMNode#getCharacters()
516          */
517         public char[] getCharacters() {
518                 CharArrayBuffer buffer = new CharArrayBuffer();
519                 appendContents(buffer);
520                 return buffer.getContents();
521         }
522
523         /**
524          * @see IDOMNode#getChild(String)
525          */
526         public IDOMNode getChild(String name) {
527                 DOMNode child = fFirstChild;
528                 while (child != null) {
529                         String n = child.getName();
530                         if (name == null) {
531                                 if (n == null) {
532                                         return child;
533                                 }
534                         } else {
535                                 if (name.equals(n)) {
536                                         return child;
537                                 }
538                         }
539                         child = child.fNextNode;
540                 }
541                 return null;
542         }
543
544         /**
545          * @see IDOMNode#getChildren()
546          */
547         public Enumeration getChildren() {
548                 return new SiblingEnumeration(fFirstChild);
549         }
550
551         /**
552          * Returns the current contents of this document fragment, or
553          * <code>null</code> if this node has no contents.
554          * 
555          * <p>
556          * If this node is fragmented, contents must be generated by using the
557          * original document and indicies as a form for the current attribute values
558          * of this node. If this node not fragmented, the contents can be obtained
559          * from the document.
560          * 
561          * @see IDOMNode#getContents()
562          */
563         public String getContents() {
564                 CharArrayBuffer buffer = new CharArrayBuffer();
565                 appendContents(buffer);
566                 return buffer.toString();
567         }
568
569         /**
570          * Returns a new document fragment representing this node with detailed
571          * source indexes. Subclasses that provide a detailed implementation must
572          * override this method.
573          */
574         protected DOMNode getDetailedNode() {
575                 return this;
576         }
577
578         /**
579          * Returns the document containing this node's original contents. The
580          * document may be shared by other nodes.
581          */
582         protected char[] getDocument() {
583                 return fDocument;
584         }
585
586         /**
587          * Returns the original position of the last character of this node's
588          * contents in its document.
589          */
590         public int getEndPosition() {
591                 return fSourceRange[1];
592         }
593
594         /**
595          * Returns a factory with which to create new document fragments.
596          */
597         protected IDOMFactory getFactory() {
598                 return new DOMFactory();
599         }
600
601         /**
602          * @see IDOMNode#getFirstChild()
603          */
604         public IDOMNode getFirstChild() {
605                 return fFirstChild;
606         }
607
608         /**
609          * Returns the position at which the first child of this node should be
610          * inserted.
611          */
612         public int getInsertionPosition() {
613                 return fInsertionPosition;
614         }
615
616         /**
617          * Returns <code>true</code> if the given mask of this node's state flag
618          * is turned on, otherwise <code>false</code>.
619          */
620         protected boolean getMask(int mask) {
621                 return (fStateMask & mask) > 0;
622         }
623
624         /**
625          * @see IDOMNode#getName()
626          */
627         public String getName() {
628                 return fName;
629         }
630
631         /**
632          * Returns the source code to be used for this node's name.
633          */
634         protected char[] getNameContents() {
635                 if (isNameAltered()) {
636                         return fName.toCharArray();
637                 } else {
638                         if (fName == null || fNameRange[0] < 0) {
639                                 return null;
640                         } else {
641                                 int length = fNameRange[1] + 1 - fNameRange[0];
642                                 char[] result = new char[length];
643                                 System.arraycopy(fDocument, fNameRange[0], result, 0, length);
644                                 return result;
645                         }
646                 }
647         }
648
649         /**
650          * @see IDOMNode#getNextNode()
651          */
652         public IDOMNode getNextNode() {
653                 return fNextNode;
654         }
655
656         /**
657          * @see IDOMNode#getParent()
658          */
659         public IDOMNode getParent() {
660                 return fParent;
661         }
662
663         /**
664          * Answers a source position which corresponds to the end of the parent
665          * element's declaration.
666          */
667         protected int getParentEndDeclaration() {
668                 IDOMNode parent = getParent();
669                 if (parent == null) {
670                         return 0;
671                 } else {
672                         if (parent instanceof IDOMCompilationUnit) {
673                                 return 0;
674                         } else {
675                                 return ((DOMType) parent).getOpenBodyEnd();
676                         }
677                 }
678         }
679
680         /**
681          * @see IDOMNode#getPreviousNode()
682          */
683         public IDOMNode getPreviousNode() {
684                 return fPreviousNode;
685         }
686
687         /**
688          * Returns the root node of this document fragment.
689          */
690         protected IDOMNode getRoot() {
691                 if (fParent == null) {
692                         return this;
693                 } else {
694                         return fParent.getRoot();
695                 }
696         }
697
698         /**
699          * Returns the original position of the first character of this node's
700          * contents in its document.
701          */
702         public int getStartPosition() {
703                 return fSourceRange[0];
704         }
705
706         /**
707          * @see IDOMNode#insertSibling(IDOMNode)
708          */
709         public void insertSibling(IDOMNode sibling)
710                         throws IllegalArgumentException, DOMException {
711                 // verify sibling may be added
712                 if (sibling == null) {
713                         throw new IllegalArgumentException(Util.bind("dom.addNullSibling")); //$NON-NLS-1$
714                 }
715                 if (fParent == null) {
716                         throw new DOMException(Util.bind("dom.addSiblingBeforeRoot")); //$NON-NLS-1$
717                 }
718                 if (!fParent.isAllowableChild(sibling)) {
719                         throw new DOMException(Util.bind("dom.addIncompatibleSibling")); //$NON-NLS-1$
720                 }
721                 if (sibling.getParent() != null) {
722                         throw new DOMException(Util.bind("dom.addSiblingWithParent")); //$NON-NLS-1$
723                 }
724                 /*
725                  * NOTE: To test if the sibling is an ancestor of this node, we need
726                  * only test if the root of this node is the child (the sibling is
727                  * already a root since we have just guaranteed it has no parent).
728                  */
729                 if (sibling == getRoot()) {
730                         throw new DOMException(Util.bind("dom.addAncestorAsSibling")); //$NON-NLS-1$
731                 }
732
733                 DOMNode node = (DOMNode) sibling;
734
735                 // if the sibling is not already part of this document, localize its
736                 // contents
737                 // before inserting it into the tree
738                 if (node.getDocument() != getDocument()) {
739                         node.localizeContents();
740                 }
741
742                 // insert the node
743                 if (fPreviousNode == null) {
744                         fParent.fFirstChild = node;
745                 } else {
746                         fPreviousNode.fNextNode = node;
747                 }
748                 node.fParent = fParent;
749                 node.fPreviousNode = fPreviousNode;
750                 node.fNextNode = this;
751                 fPreviousNode = node;
752
753                 // if the node is a constructor, it must also be fragmented to update
754                 // the constructor's name
755                 if (node.getNodeType() == IDOMNode.METHOD
756                                 && ((IDOMMethod) node).isConstructor()) {
757                         node.fragment();
758                 } else {
759                         fParent.fragment();
760                 }
761         }
762
763         /**
764          * @see IDOMNode
765          */
766         public boolean isAllowableChild(IDOMNode node) {
767                 return false;
768         }
769
770         /**
771          * Returns <code>true</code> if the contents of this node are from the
772          * same document as the given node, the contents of this node immediately
773          * follow the contents of the given node, and neither this node or the given
774          * node are fragmented - otherwise <code>false</code>.
775          */
776         protected boolean isContentMergableWith(DOMNode node) {
777                 return !node.isFragmented() && !isFragmented()
778                                 && node.getDocument() == getDocument()
779                                 && node.getEndPosition() + 1 == getStartPosition();
780         }
781
782         /**
783          * Returns <code>true</code> if this node has detailed source index
784          * information, or <code>false</code> if this node has limited source
785          * index information. To perform some manipulations, detailed indexes are
786          * required.
787          */
788         protected boolean isDetailed() {
789                 return getMask(MASK_DETAILED_SOURCE_INDEXES);
790         }
791
792         /**
793          * Returns <code>true</code> if this node's or a descendant node's
794          * contents have been altered since this node was created. This indicates
795          * that the contents of this node are no longer consistent with the contents
796          * of this node's document.
797          */
798         protected boolean isFragmented() {
799                 return fIsFragmented;
800         }
801
802         /**
803          * Returns <code>true</code> if this noed's name has been altered from the
804          * original document contents.
805          */
806         protected boolean isNameAltered() {
807                 return getMask(MASK_NAME_ALTERED);
808         }
809
810         /**
811          * @see IDOMNode#isSignatureEqual(IDOMNode).
812          * 
813          * <p>
814          * By default, the signatures of two nodes are equal if their type and names
815          * are equal. Node types that have other requirements for equality must
816          * override this method.
817          */
818         public boolean isSignatureEqual(IDOMNode node) {
819                 return getNodeType() == node.getNodeType()
820                                 && getName().equals(node.getName());
821         }
822
823         /**
824          * Localizes the contents of this node and all descendant nodes, such that
825          * this node is no longer dependent on its original document in order to
826          * generate its contents. This node and all descendant nodes become
827          * unfragmented and share a new document.
828          */
829         protected void localizeContents() {
830
831                 DOMNode clone = (DOMNode) clone();
832                 shareContents(clone);
833
834         }
835
836         /**
837          * Returns a new empty <code>DOMNode</code> for this instance.
838          */
839         protected abstract DOMNode newDOMNode();
840
841         /**
842          * Normalizes this <code>DOMNode</code>'s source positions to include
843          * whitespace preceeding the node on the line on which the node starts, and
844          * all whitespace after the node up to the next node's start
845          */
846         void normalize(ILineStartFinder finder) {
847                 if (getPreviousNode() == null)
848                         normalizeStartPosition(getParentEndDeclaration(), finder);
849
850                 // Set the children's position
851                 if (canHaveChildren()) {
852                         Enumeration children = getChildren();
853                         while (children.hasMoreElements())
854                                 ((DOMNode) children.nextElement()).normalize(finder);
855                 }
856
857                 normalizeEndPosition(finder, (DOMNode) getNextNode());
858         }
859
860         /**
861          * Normalizes this <code>DOMNode</code>'s end position.
862          */
863         void normalizeEndPosition(ILineStartFinder finder, DOMNode next) {
864                 if (next == null) {
865                         // this node's end position includes all of the characters up
866                         // to the end of the enclosing node
867                         DOMNode parent = (DOMNode) getParent();
868                         if (parent == null || parent instanceof DOMCompilationUnit) {
869                                 setSourceRangeEnd(fDocument.length - 1);
870                         } else {
871                                 // parent is a type
872                                 int temp = ((DOMType) parent).getCloseBodyPosition() - 1;
873                                 setSourceRangeEnd(temp);
874                                 fInsertionPosition = Math.max(finder.getLineStart(temp + 1),
875                                                 getEndPosition());
876                         }
877                 } else {
878                         // this node's end position is just before the start of the next
879                         // node
880                         int temp = next.getStartPosition() - 1;
881                         fInsertionPosition = Math.max(finder.getLineStart(temp + 1),
882                                         getEndPosition());
883                         next.normalizeStartPosition(getEndPosition(), finder);
884                         setSourceRangeEnd(next.getStartPosition() - 1);
885                 }
886         }
887
888         /**
889          * Normalizes this <code>DOMNode</code>'s start position.
890          */
891         void normalizeStartPosition(int previousEnd, ILineStartFinder finder) {
892                 int nodeStart = getStartPosition();
893                 int lineStart = finder.getLineStart(nodeStart);
894                 if (nodeStart > lineStart
895                                 && (lineStart > previousEnd || (previousEnd == 0 && lineStart == 0)))
896                         setStartPosition(lineStart);
897         }
898
899         /**
900          * Offsets all the source indexes in this node by the given amount.
901          */
902         protected void offset(int offset) {
903                 offsetRange(fNameRange, offset);
904                 offsetRange(fSourceRange, offset);
905         }
906
907         /**
908          * Offsets the source range by the given amount
909          */
910         protected void offsetRange(int[] range, int offset) {
911                 for (int i = 0; i < range.length; i++) {
912                         range[i] += offset;
913                         if (range[i] < 0) {
914                                 range[i] = -1;
915                         }
916                 }
917         }
918
919         /**
920          * Returns a copy of the given range.
921          */
922         protected int[] rangeCopy(int[] range) {
923                 int[] copy = new int[range.length];
924                 for (int i = 0; i < range.length; i++) {
925                         copy[i] = range[i];
926                 }
927                 return copy;
928         }
929
930         /**
931          * Separates this node from its parent and siblings, maintaining any ties
932          * that this node has to the underlying document fragment.
933          * 
934          * <p>
935          * When a child is removed, its parent is fragmented such that it properly
936          * generates its contents.
937          * 
938          * @see IDOMNode#remove()
939          */
940         public void remove() {
941
942                 if (fParent != null) {
943                         fParent.fragment();
944                 }
945
946                 // link siblings
947                 if (fNextNode != null) {
948                         fNextNode.fPreviousNode = fPreviousNode;
949                 }
950                 if (fPreviousNode != null) {
951                         fPreviousNode.fNextNode = fNextNode;
952                 }
953                 // fix parent's pointers
954                 if (fParent != null) {
955                         if (fParent.fFirstChild == this) {
956                                 fParent.fFirstChild = fNextNode;
957                         }
958                         if (fParent.fLastChild == this) {
959                                 fParent.fLastChild = fPreviousNode;
960                         }
961                 }
962                 // remove myself
963                 fParent = null;
964                 fNextNode = null;
965                 fPreviousNode = null;
966         }
967
968         /**
969          * Sets the specified mask of this node's state mask on or off based on the
970          * boolean value - true -> on, false -> off.
971          */
972         protected void setMask(int mask, boolean on) {
973                 if (on) {
974                         fStateMask |= mask;
975                 } else {
976                         fStateMask &= ~mask;
977                 }
978         }
979
980         /**
981          * @see IDOMNode#setName
982          */
983         public void setName(String name) {
984                 fName = name;
985                 setNameAltered(true);
986                 fragment();
987         }
988
989         /**
990          * Sets the state of this node as having its name attribute altered from the
991          * original document contents.
992          */
993         protected void setNameAltered(boolean altered) {
994                 setMask(MASK_NAME_ALTERED, altered);
995         }
996
997         /**
998          * Sets the original position of the last character of this node's contents
999          * in its document. This method is only used during DOM creation while
1000          * normalizing the source range of each node.
1001          */
1002         protected void setSourceRangeEnd(int end) {
1003                 fSourceRange[1] = end;
1004         }
1005
1006         /**
1007          * Sets the original position of the first character of this node's contents
1008          * in its document. This method is only used during DOM creation while
1009          * normalizing the source range of each node.
1010          */
1011         protected void setStartPosition(int start) {
1012                 fSourceRange[0] = start;
1013         }
1014
1015         /**
1016          * Sets the contents of this node and descendant nodes to be the (identical)
1017          * contents of the given node and its descendants. This does not effect this
1018          * node's parent and sibling configuration, only the contents of this node.
1019          * This is used only to localize the contents of this node.
1020          */
1021         protected void shareContents(DOMNode node) {
1022                 fDocument = node.fDocument;
1023                 fIsFragmented = node.fIsFragmented;
1024                 fName = node.fName;
1025                 fNameRange = rangeCopy(node.fNameRange);
1026                 fSourceRange = rangeCopy(node.fSourceRange);
1027                 fStateMask = node.fStateMask;
1028
1029                 if (canHaveChildren()) {
1030                         Enumeration myChildren = getChildren();
1031                         Enumeration otherChildren = node.getChildren();
1032                         DOMNode myChild, otherChild;
1033                         while (myChildren.hasMoreElements()) {
1034                                 myChild = (DOMNode) myChildren.nextElement();
1035                                 otherChild = (DOMNode) otherChildren.nextElement();
1036                                 myChild.shareContents(otherChild);
1037                         }
1038                 }
1039         }
1040
1041         /**
1042          * Returns a <code>String</code> representing this node - for Debug
1043          * purposes only.
1044          */
1045         public abstract String toString();
1046 }