new version with WorkingCopy Management
[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>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.
31  * 
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).
38  *
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
46  * as required.
47  *
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
53  * node. 
54  *
55  * @see IDOMNode
56  */
57 public abstract class DOMNode implements IDOMNode {
58
59         /**
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).
63          */
64         protected DOMNode fFirstChild= null;
65
66         /**
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.
71          */
72         protected DOMNode fLastChild= null;
73
74         /**
75          * The sibling node following this node - <code>null</code>
76          * for the last node in the sibling list.
77          */
78         protected DOMNode fNextNode= null;
79
80         /**
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.
84          */
85         protected DOMNode fParent= null;
86
87         /**
88          * The sibling node preceding this node - <code>null</code>
89          * for the first node in the sibling list.
90          */
91         protected DOMNode fPreviousNode= null;
92
93         /**
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 
100          * document.
101          */
102         protected boolean fIsFragmented= false;
103
104         /**
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.
109          */
110         protected String  fName= null;
111
112         /**
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.
116          */
117         protected int[]   fNameRange;
118
119         /**
120          * The shared document that the contents for this node
121          * are contained in. Attribute indicies are positions
122          * in this character array.
123          */
124         protected char[]  fDocument= null;
125
126         /**
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.
130          */
131         protected int[] fSourceRange;
132
133         /**
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.
138          *
139          * @see #setMask
140          * @see #getMask
141          */
142         protected int fStateMask= 0;
143
144         /**
145          * This position is the position of the end of the last line separator before the closing brace starting
146          * position of the receiver.
147          */
148         protected int fInsertionPosition;
149
150         /**
151          * A bit mask indicating this field has an initializer
152          * expression
153          */
154         protected static final int MASK_FIELD_HAS_INITIALIZER= 0x00000001;
155         
156         /**
157          * A bit mask indicating this field is a secondary variable
158          * declarator for a previous field declaration.
159          */
160         protected static final int MASK_FIELD_IS_VARIABLE_DECLARATOR= 0x00000002;
161
162         /**
163          * A bit mask indicating this field's type has been
164          * altered from its original contents in the document.
165          */
166         protected static final int MASK_FIELD_TYPE_ALTERED= 0x00000004;
167
168         /**
169          * A bit mask indicating this node's name has been
170          * altered from its original contents in the document.
171          */
172         protected static final int MASK_NAME_ALTERED= 0x00000008;
173
174         /**
175          * A bit mask indicating this node currently has a
176          * body.
177          */
178         protected static final int MASK_HAS_BODY= 0x00000010;
179         
180         /**
181          * A bit mask indicating this node currently has a
182          * preceding comment.
183          */
184         protected static final int MASK_HAS_COMMENT= 0x00000020;
185
186         /**
187          * A bit mask indicating this method is a constructor.
188          */
189         protected static final int MASK_IS_CONSTRUCTOR= 0x00000040;
190
191         /**
192          * A bit mask indicating this type is a class.
193          */
194         protected static final int MASK_TYPE_IS_CLASS= 0x00000080;
195
196         /**
197          * A bit mask indicating this type has a superclass
198          * (requires or has an 'extends' clause).
199          */
200         protected static final int MASK_TYPE_HAS_SUPERCLASS= 0x00000100;
201
202         /**
203          * A bit mask indicating this type implements
204          * or extends some interfaces
205          */
206         protected static final int MASK_TYPE_HAS_INTERFACES= 0x00000200;
207
208         /**
209          * A bit mask indicating this return type of this method has
210          * been altered from the original contents.
211          */
212         protected static final int MASK_RETURN_TYPE_ALTERED= 0x00000400;
213
214         /**
215          * A bit mask indicating this node has detailed source indexes
216          */
217         protected static final int MASK_DETAILED_SOURCE_INDEXES = 0x00000800;
218
219 /**
220  * Creates a new empty document fragment.
221  */
222 DOMNode() {
223         fName= null;
224         fDocument= null;
225         fSourceRange= new int[]{-1, -1};
226         fNameRange= new int[]{-1, -1};
227         fragment();
228 }
229 /**
230  * Creates a new document fragment on the given range of the document.
231  *
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
238  *              in the document.
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.
245  */
246 DOMNode(char[] document, int[] sourceRange, String name, int[] nameRange) {
247         super();
248         fDocument= document;
249         fSourceRange= sourceRange;
250         fName= name;
251         fNameRange= nameRange;
252
253 }
254 /**
255  * Adds the given un-parented node (document fragment) as the last child of
256  * this node.
257  *
258  * <p>When a child is added, this node must be considered fragmented such that
259  * the contents of this node are properly generated.
260  * 
261  * @see IDOMNode#addChild(IDOMNode)
262  */
263 public void addChild(IDOMNode child) throws IllegalArgumentException, DOMException {
264         basicAddChild(child);
265         
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();
269         } else {
270                 fragment();
271         }
272 }
273 /**
274  * Appends the current contents of this document fragment
275  * to the given <code>CharArrayBuffer</code>.
276  *
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.
281  * 
282  */
283 protected void appendContents(CharArrayBuffer buffer) {
284         if (isFragmented()) {
285                 appendFragmentedContents(buffer);
286         } else {
287                 buffer.append(fDocument, fSourceRange[0], fSourceRange[1] + 1 - fSourceRange[0]);
288         }
289 }
290 /**
291  * Appends the contents of all children of this node to the
292  * given <code>CharArrayBuffer</code>.
293  *
294  * <p>This algorithm used minimizes String generation by merging
295  * adjacent unfragmented children into one substring operation.
296  *
297  */
298 protected void appendContentsOfChildren(CharArrayBuffer buffer) {
299         DOMNode child= fFirstChild;
300         DOMNode sibling;
301         
302         int start= 0, end= 0;
303         if (child != null) {
304                 start= child.getStartPosition();
305                 end= child.getEndPosition();
306         }
307         while (child != null) {
308                 sibling= child.fNextNode;
309                 if (sibling != null) {
310                         if (sibling.isContentMergableWith(child)) {
311                                 end= sibling.getEndPosition();
312                         } else {
313                                 if (child.isFragmented()) {
314                                         child.appendContents(buffer);
315                                 } else {
316                                         buffer.append(child.getDocument(), start, end + 1 - start);
317                                 }
318                                 start= sibling.getStartPosition();
319                                 end= sibling.getEndPosition();
320                         }
321                 } else {
322                         if (child.isFragmented()) {
323                                 child.appendContents(buffer);
324                         } else {
325                                 buffer.append(child.getDocument(), start, end + 1 - start);
326                         }
327                 }
328                 child= sibling;
329         }
330 }
331 /**
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.
335  */
336 protected abstract void appendFragmentedContents(CharArrayBuffer buffer);
337 /**
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.
342  */
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$
347         }
348         if (child == null) {
349                 throw new IllegalArgumentException(Util.bind("dom.addNullChild")); //$NON-NLS-1$
350         }
351         if (!isAllowableChild(child)) {
352                 throw new DOMException(Util.bind("dom.addIncompatibleChild")); //$NON-NLS-1$
353         }
354         if (child.getParent() != null) {
355                 throw new DOMException(Util.bind("dom.addChildWithParent")); //$NON-NLS-1$
356         }
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).
360          */
361         if (child == getRoot()) {
362                 throw new DOMException(Util.bind("dom.addAncestorAsChild")); //$NON-NLS-1$
363         }
364
365         DOMNode node= (DOMNode)child;
366         
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();
371         }
372         
373         // add the child last
374         if (fFirstChild == null) {
375                 // this is the first and only child
376                 fFirstChild= node;
377         } else {
378                 fLastChild.fNextNode= node;
379                 node.fPreviousNode= fLastChild;
380         }
381         fLastChild= node;
382         node.fParent= this;
383 }
384 /**
385  * Generates detailed source indexes for this node if possible.
386  *
387  * @exception DOMException if unable to generate detailed source indexes
388  *      for this node
389  */
390 protected void becomeDetailed() throws DOMException {
391         if (!isDetailed()) {
392                 DOMNode detailed= getDetailedNode();
393                 if (detailed == null) {
394                         throw new DOMException(Util.bind("dom.cannotDetail")); //$NON-NLS-1$
395                 }
396                 if (detailed != this) {
397                         shareContents(detailed);
398                 }
399         }
400 }
401 /**
402  * Returns true if this node is allowed to have children, otherwise false.
403  *
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
406  * children.
407  *
408  * @see IDOMNode#canHaveChildren()
409  */
410 public boolean canHaveChildren() {
411         return false;
412 }
413 /**
414  * @see IDOMNode#clone()
415  */
416 public Object clone() {
417
418         // create a new buffer with all my contents and children contents
419         int length= 0;
420         char[] buffer= null;
421         int offset= fSourceRange[0];
422         
423         if (offset >= 0) {
424                 length= fSourceRange[1] - offset + 1;
425                 buffer= new char[length];
426                 System.arraycopy(fDocument, offset, buffer, 0, length);
427         }       
428         DOMNode clone= newDOMNode();
429         clone.shareContents(this);
430         clone.fDocument = buffer;
431
432         if (offset > 0) {
433                 clone.offset(0 - offset);
434         }
435
436         // clone my children
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);
444                         } else {
445                                 DOMNode childClone= (DOMNode)child.clone();
446                                 clone.addChild(childClone);
447                         }
448                         
449                 }
450         }
451         
452         return clone;
453 }
454 private DOMNode cloneSharingDocument(char[] document, int rootOffset) {
455
456         DOMNode clone = newDOMNode();
457         clone.shareContents(this);
458         clone.fDocument = document;
459         if (rootOffset > 0) {
460                 clone.offset(0 - rootOffset);
461         }
462         
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);
470                         } else {
471                                 DOMNode childClone= (DOMNode)child.clone();
472                                 clone.addChild(childClone);
473                         }
474                 }
475         }
476         return clone;
477 }
478 /**
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.
484  */
485 protected void fragment() {
486         if (!isFragmented()) {
487                 fIsFragmented= true;
488                 if (fParent != null) {
489                         fParent.fragment();
490                 }
491         }
492 }
493 /**
494  * @see IDOMNode#getCharacters()
495  */
496 public char[] getCharacters() {
497         CharArrayBuffer buffer= new CharArrayBuffer();
498         appendContents(buffer);
499         return buffer.getContents();
500 }
501 /**
502  * @see IDOMNode#getChild(String)
503  */
504 public IDOMNode getChild(String name) {
505         DOMNode child = fFirstChild;
506         while (child != null) {
507                 String n = child.getName();
508                 if (name == null) {
509                         if (n == null) {
510                                 return child;
511                         }
512                 } else {
513                         if (name.equals(n)) {
514                                 return child;
515                         }
516                 }
517                 child = child.fNextNode;
518         }
519         return null;
520 }
521 /**
522  * @see IDOMNode#getChildren()
523  */
524 public Enumeration getChildren() {
525         return new SiblingEnumeration(fFirstChild);
526 }
527 /**
528  * Returns the current contents of this document fragment,
529  * or <code>null</code> if this node has no contents.
530  *
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.
535  * 
536  * @see IDOMNode#getContents()
537  */
538 public String getContents() {
539         CharArrayBuffer buffer= new CharArrayBuffer();
540         appendContents(buffer);
541         return buffer.toString();
542 }
543 /**
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.
547  */
548 protected DOMNode getDetailedNode() {
549         return this;
550 }
551 /**
552  * Returns the document containing this node's original contents.
553  * The document may be shared by other nodes.
554  */
555 protected char[] getDocument() {
556         return fDocument;
557 }
558 /**
559  * Returns the original position of the last character of this
560  * node's contents in its document.
561  */
562 public int getEndPosition() {
563         return fSourceRange[1];
564 }
565 /**
566  * Returns a factory with which to create new document fragments.
567  */
568 protected IDOMFactory getFactory() {
569         return new DOMFactory();
570 }
571 /**
572  * @see IDOMNode#getFirstChild()
573  */
574 public IDOMNode getFirstChild() {
575         return fFirstChild;
576 }
577 /**
578  * Returns the position at which the first child of this node should be inserted.
579  */
580 public int getInsertionPosition() {
581         return fInsertionPosition;
582 }
583 /**
584  * Returns <code>true</code> if the given mask of this node's state flag
585  * is turned on, otherwise <code>false</code>.
586  */
587 protected boolean getMask(int mask) {
588         return (fStateMask & mask) > 0;
589 }
590 /**
591  * @see IDOMNode#getName()
592  */
593 public String getName() {
594         return fName;
595 }
596 /**
597  * Returns the source code to be used for this node's name.
598  */
599 protected char[] getNameContents() {
600         if (isNameAltered()) {
601                 return fName.toCharArray();
602         } else {
603                 if (fName == null || fNameRange[0] < 0) {
604                         return null;
605                 } else {
606                         int length = fNameRange[1] + 1 - fNameRange[0];
607                         char[] result = new char[length];
608                         System.arraycopy(fDocument, fNameRange[0], result, 0, length);
609                         return result;
610                 }
611         }
612 }
613 /**
614  * @see IDOMNode#getNextNode()
615  */
616 public IDOMNode getNextNode() {
617         return fNextNode;
618 }
619 /**
620  * @see IDOMNode#getParent()
621  */
622 public IDOMNode getParent() {
623         return fParent;
624 }
625 /**
626  * Answers a source position which corresponds to the end of the parent
627  * element's declaration.
628  */
629 protected int getParentEndDeclaration() {
630         IDOMNode parent = getParent();
631         if (parent == null) {
632                 return 0;
633         } else {
634                 if (parent instanceof IDOMCompilationUnit) {
635                         return 0;
636                 } else {
637                         return ((DOMType)parent).getOpenBodyEnd();
638                 }
639         }
640 }
641 /**
642  * @see IDOMNode#getPreviousNode()
643  */
644 public IDOMNode getPreviousNode() {
645         return fPreviousNode;
646 }
647 /**
648  * Returns the root node of this document fragment.
649  */
650 protected IDOMNode getRoot() {
651         if (fParent == null) {
652                 return this;
653         } else {
654                 return fParent.getRoot();
655         }
656 }
657 /**
658  * Returns the original position of the first character of this
659  * node's contents in its document.
660  */
661 public int getStartPosition() {
662         return fSourceRange[0];
663 }
664 /**
665  * @see IDOMNode#insertSibling(IDOMNode)
666  */
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$
671         }
672         if (fParent == null) {
673                 throw new DOMException(Util.bind("dom.addSiblingBeforeRoot")); //$NON-NLS-1$
674         }
675         if (!fParent.isAllowableChild(sibling)) {
676                 throw new DOMException(Util.bind("dom.addIncompatibleSibling")); //$NON-NLS-1$
677         }
678         if (sibling.getParent() != null) {
679                 throw new DOMException(Util.bind("dom.addSiblingWithParent")); //$NON-NLS-1$
680         }
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).
684          */
685         if (sibling == getRoot()) {
686                 throw new DOMException(Util.bind("dom.addAncestorAsSibling")); //$NON-NLS-1$
687         }
688
689         DOMNode node= (DOMNode)sibling;
690         
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();
695         }
696
697         // insert the node
698         if (fPreviousNode == null) {
699                 fParent.fFirstChild= node;
700         } else {
701                 fPreviousNode.fNextNode= node;  
702         }
703         node.fParent= fParent;
704         node.fPreviousNode= fPreviousNode;
705         node.fNextNode= this;
706         fPreviousNode= node;
707
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()) {
710                 node.fragment();
711         } else {
712                 fParent.fragment();
713         }
714 }
715 /**
716  * @see IDOMNode
717  */
718 public boolean isAllowableChild(IDOMNode node) {
719         return false;
720 }
721 /**
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>.
726  */
727 protected boolean isContentMergableWith(DOMNode node) {
728         return !node.isFragmented() && !isFragmented() && node.getDocument() == getDocument() &&
729                 node.getEndPosition() + 1 == getStartPosition();
730 }
731 /**
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.
735  */
736 protected boolean isDetailed() {
737         return getMask(MASK_DETAILED_SOURCE_INDEXES);
738 }
739 /**
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.
744  */
745 protected boolean isFragmented() {
746         return fIsFragmented;
747 }
748 /**
749  * Returns <code>true</code> if this noed's name has been altered
750  * from the original document contents.
751  */
752 protected boolean isNameAltered() {
753         return getMask(MASK_NAME_ALTERED);
754 }
755 /**
756  * @see IDOMNode#isSignatureEqual(IDOMNode).
757  *
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.
761  */
762 public boolean isSignatureEqual(IDOMNode node) {
763         return getNodeType() == node.getNodeType() && getName().equals(node.getName());
764 }
765 /**
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
770  * document.
771  */
772 protected void localizeContents() {
773
774         DOMNode clone= (DOMNode)clone();
775         shareContents(clone);
776
777 }
778 /**
779  * Returns a new empty <code>DOMNode</code> for this instance.
780  */
781 protected abstract DOMNode newDOMNode();
782 /**
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
786  */
787 void normalize(ILineStartFinder finder) {
788         if (getPreviousNode() == null)
789                 normalizeStartPosition(getParentEndDeclaration(), finder);
790
791         // Set the children's position
792         if (canHaveChildren()) {
793                 Enumeration children = getChildren();
794                 while(children.hasMoreElements())
795                         ((DOMNode)children.nextElement()).normalize(finder);
796         }
797
798         normalizeEndPosition(finder, (DOMNode)getNextNode());
799 }
800 /**
801  * Normalizes this <code>DOMNode</code>'s end position.
802  */
803 void normalizeEndPosition(ILineStartFinder finder, DOMNode next) {
804         if (next == null) {
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);
810                 } else {
811                         // parent is a type
812                         int temp = ((DOMType)parent).getCloseBodyPosition() - 1;
813                         setSourceRangeEnd(temp);
814                         fInsertionPosition = Math.max(finder.getLineStart(temp + 1), getEndPosition());
815                 }
816         } else {
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);
822         }
823 }
824 /**
825  * Normalizes this <code>DOMNode</code>'s start position.
826  */
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);                    
832 }
833 /**
834  * Offsets all the source indexes in this node by the given amount.
835  */
836 protected void offset(int offset) {
837         offsetRange(fNameRange, offset);
838         offsetRange(fSourceRange, offset);
839 }
840 /**
841  * Offsets the source range by the given amount
842  */
843 protected void offsetRange(int[] range, int offset) {
844         for (int i= 0; i < range.length; i++) {
845                 range[i]+=offset;
846                 if (range[i] < 0) {
847                         range[i]= -1;
848                 }
849         }
850 }
851 /**
852  * Returns a copy of the given range.
853  */
854 protected int[] rangeCopy(int[] range) {
855         int[] copy= new int[range.length];
856         for (int i= 0; i < range.length; i++) {
857                 copy[i]= range[i];
858         }
859         return copy;
860 }
861 /**
862  * Separates this node from its parent and siblings, maintaining any ties that
863  * this node has to the underlying document fragment.
864  *
865  * <p>When a child is removed, its parent is fragmented such that it properly
866  * generates its contents.
867  *
868  * @see IDOMNode#remove()
869  */
870 public void remove() {
871
872         if (fParent != null) {
873                 fParent.fragment();
874         }
875         
876         // link siblings
877         if (fNextNode != null) {
878                 fNextNode.fPreviousNode= fPreviousNode;
879         }
880         if (fPreviousNode != null) {
881                 fPreviousNode.fNextNode= fNextNode;
882         }
883         // fix parent's pointers
884         if (fParent != null) {
885                 if (fParent.fFirstChild == this) {
886                         fParent.fFirstChild= fNextNode;
887                 }
888                 if (fParent.fLastChild == this) {
889                         fParent.fLastChild= fPreviousNode;
890                 }
891         }
892         // remove myself
893         fParent= null;
894         fNextNode= null;
895         fPreviousNode= null;
896 }
897 /**
898  * Sets the specified mask of this node's state mask on or off
899  * based on the boolean value - true -> on, false -> off.
900  */
901 protected void setMask(int mask, boolean on) {
902         if (on) {
903                 fStateMask |= mask;
904         } else {
905                 fStateMask &= ~mask;
906         }
907 }
908 /**
909  * @see IDOMNode#setName
910  */
911 public void setName(String name) {
912         fName= name;
913         setNameAltered(true);
914         fragment();
915 }
916 /**
917  * Sets the state of this node as having
918  * its name attribute altered from the original
919  * document contents.
920  */
921 protected void setNameAltered(boolean altered) {
922         setMask(MASK_NAME_ALTERED, altered);
923 }
924 /**
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.
928  */
929 protected void setSourceRangeEnd(int end) {
930         fSourceRange[1]= end;
931 }
932 /**
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.
936  */
937 protected void setStartPosition(int start) {
938         fSourceRange[0]= start;
939 }
940 /**
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.
946  */
947 protected void shareContents(DOMNode node) {
948         fDocument= node.fDocument;
949         fIsFragmented= node.fIsFragmented;
950         fName= node.fName;
951         fNameRange= rangeCopy(node.fNameRange);
952         fSourceRange= rangeCopy(node.fSourceRange);
953         fStateMask= node.fStateMask;
954
955         
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);
964                 }
965         }
966 }
967 /**
968  * Returns a <code>String</code> representing this node - for Debug purposes only.
969  */
970 public abstract String toString();
971 }