f40cff6dac92f8ff8fcb0ef6c4dd2e55eb3012ca
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / jdom / DOMField.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.IJavaElement;
16 import net.sourceforge.phpdt.core.IType;
17 import net.sourceforge.phpdt.core.jdom.DOMException;
18 import net.sourceforge.phpdt.core.jdom.IDOMField;
19 import net.sourceforge.phpdt.core.jdom.IDOMMember;
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 import net.sourceforge.phpdt.internal.core.util.CharArrayOps;
24
25 /**
26  * DOMField provides an implementation of IDOMField.
27  * 
28  * @see IDOMField
29  * @see DOMNode
30  */
31 class DOMField extends DOMMember implements IDOMField {
32
33         /**
34          * Contains the type of the field when the type has been altered from the
35          * contents in the document, otherwise <code>null</code>.
36          */
37         protected String fType;
38
39         /**
40          * The original inclusive source range of the field's type in the document.
41          */
42         protected int[] fTypeRange;
43
44         /**
45          * The contents of the initializer when the initializer has been altered
46          * from the original state in the document, otherwise <code>null</code>.
47          */
48         protected String fInitializer;
49
50         /**
51          * The original inclusive source range of the initializer in the document.
52          */
53         protected int[] fInitializerRange;
54
55         /**
56          * Constructs an empty field node.
57          */
58         DOMField() {
59         }
60
61         /**
62          * Creates a new detailed FIELD document fragment on the given range of the
63          * document.
64          * 
65          * @param document -
66          *            the document containing this node's original contents
67          * @param sourceRange -
68          *            a two element array of integers describing the entire
69          *            inclusive source range of this node within its document.
70          *            Contents start on and include the character at the first
71          *            position. Contents end on and include the character at the
72          *            last position. An array of -1's indicates this node's contents
73          *            do not exist in the document.
74          * @param name -
75          *            the identifier portion of the name of this field,
76          *            corresponding to VariableDeclaratorId (JLS 8.3).
77          * @param nameRange -
78          *            a two element array of integers describing the entire
79          *            inclusive source range of this node's name within its
80          *            document, including any array qualifiers that might follow the
81          *            name.
82          * @param commentRange -
83          *            a two element array describing the comments that precede the
84          *            member declaration. The first matches the start of this node's
85          *            sourceRange, and the second is the new-line or first
86          *            non-whitespace character following the last comment. If no
87          *            comments are present, this array contains two -1's.
88          * @param flags -
89          *            an integer representing the modifiers for this member. The
90          *            integer can be analyzed with net.sourceforge.phpdt.core.Flags
91          * @param modifierRange -
92          *            a two element array describing the location of modifiers for
93          *            this member within its source range. The first integer is the
94          *            first character of the first modifier for this member, and the
95          *            second integer is the last whitespace character preceeding the
96          *            next part of this member declaration. If there are no
97          *            modifiers present in this node's source code (that is, package
98          *            default visibility), this array contains two -1's.
99          * @param typeRange-
100          *            a two element array describing the location of the typeName in
101          *            the document - the positions of the first and last characters
102          *            of the typeName.
103          * @param type -
104          *            the type of the field, in normalized form, as defined in Type
105          *            in Field Declaration (JLS 8.3)
106          * @param hasInitializer -
107          *            true if this field declaration includes an initializer,
108          *            otherwise false
109          * @param initRange -
110          *            a two element array describing the location of the initializer
111          *            in the declaration. The first integer is the position of the
112          *            character following the equals sign, and the position of the
113          *            last character is the last in the initializer. If this field
114          *            has no initializer, this array contains two -1's.
115          * @param isVariableDeclarator -
116          *            true if the field is a seconday variable declarator for a
117          *            previous field declaration.
118          */
119         DOMField(char[] document, int[] sourceRange, String name, int[] nameRange,
120                         int[] commentRange, int flags, int[] modifierRange,
121                         int[] typeRange, String type, boolean hasInitializer,
122                         int[] initRange, boolean isVariableDeclarator) {
123                 super(document, sourceRange, name, nameRange, commentRange, flags,
124                                 modifierRange);
125
126                 fType = type;
127                 fTypeRange = typeRange;
128                 setHasInitializer(hasInitializer);
129                 fInitializerRange = initRange;
130                 setIsVariableDeclarator(isVariableDeclarator);
131                 setMask(MASK_DETAILED_SOURCE_INDEXES, true);
132
133         }
134
135         /**
136          * Creates a new simple FIELD document fragment on the given range of the
137          * document.
138          * 
139          * @param document -
140          *            the document containing this node's original contents
141          * @param sourceRange -
142          *            a two element array of integers describing the entire
143          *            inclusive source range of this node within its document.
144          *            Contents start on and include the character at the first
145          *            position. Contents end on and include the character at the
146          *            last position. An array of -1's indicates this node's contents
147          *            do not exist in the document.
148          * @param name -
149          *            the identifier portion of the name of this field,
150          *            corresponding to VariableDeclaratorId (JLS 8.3).
151          * @param nameRange -
152          *            a two element array of integers describing the entire
153          *            inclusive source range of this node's name within its
154          *            document, including any array qualifiers that might follow the
155          *            name.
156          * @param flags -
157          *            an integer representing the modifiers for this member. The
158          *            integer can be analyzed with net.sourceforge.phpdt.core.Flags
159          * @param type -
160          *            the type of the field, in normalized form, as defined in Type
161          *            in Field Declaration (JLS 8.3)
162          * @param isVariableDeclarator -
163          *            true if the field is a seconday variable declarator for a
164          *            previous field declaration.
165          */
166         DOMField(char[] document, int[] sourceRange, String name, int[] nameRange,
167                         int flags, String type, boolean isVariableDeclarator) {
168                 this(document, sourceRange, name, nameRange, new int[] { -1, -1 },
169                                 flags, new int[] { -1, -1 }, new int[] { -1, -1 }, type, false,
170                                 new int[] { -1, -1 }, isVariableDeclarator);
171                 setMask(MASK_DETAILED_SOURCE_INDEXES, false);
172         }
173
174         /**
175          * Appends this member's body contents to the given CharArrayBuffer. Body
176          * contents include the member body and any trailing whitespace.
177          * 
178          * <p>
179          * A field does not have a body.
180          * 
181          * @see DOMMember#appendMemberBodyContents(CharArrayBuffer)
182          */
183         protected void appendMemberBodyContents(CharArrayBuffer buffer) {
184         }
185
186         /**
187          * @see DOMMember#appendMemberDeclarationContents(CharArrayBuffer)
188          */
189         protected void appendMemberDeclarationContents(CharArrayBuffer buffer) {
190
191                 if (isVariableDeclarator()) {
192                         buffer.append(fDocument, fSourceRange[0], fNameRange[0]
193                                         - fSourceRange[0]);
194                 } else {
195                         buffer.append(getTypeContents()).append(fDocument,
196                                         fTypeRange[1] + 1, fNameRange[0] - fTypeRange[1] - 1);
197                 }
198
199                 buffer.append(getNameContents());
200                 if (hasInitializer()) {
201                         if (fInitializerRange[0] < 0) {
202                                 buffer.append('=').append(fInitializer).append(fDocument,
203                                                 fNameRange[1] + 1, fSourceRange[1] - fNameRange[1]);
204                         } else {
205                                 buffer.append(fDocument, fNameRange[1] + 1,
206                                                 fInitializerRange[0] - fNameRange[1] - 1).append(
207                                                 getInitializer()).append(fDocument,
208                                                 fInitializerRange[1] + 1,
209                                                 fSourceRange[1] - fInitializerRange[1]);
210                         }
211                 } else {
212                         if (fInitializerRange[0] < 0) {
213                                 buffer.append(fDocument, fNameRange[1] + 1, fSourceRange[1]
214                                                 - fNameRange[1]);
215                         } else {
216                                 buffer.append(fDocument, fInitializerRange[1] + 1,
217                                                 fSourceRange[1] - fInitializerRange[1]);
218                         }
219                 }
220
221         }
222
223         /**
224          * Appends this member's header contents to the given CharArrayBuffer.
225          * Header contents include any preceding comments and modifiers.
226          * 
227          * <p>
228          * If this field is a secondary variable declarator, there is no header.
229          * 
230          * @see DOMMember#appendMemberHeaderFragment(CharArrayBuffer)
231          */
232         protected void appendMemberHeaderFragment(CharArrayBuffer buffer) {
233
234                 if (isVariableDeclarator()) {
235                         return;
236                 } else {
237                         super.appendMemberHeaderFragment(buffer);
238                 }
239
240         }
241
242         /**
243          * @see DOMMember#appendSimpleContents(CharArrayBuffer)
244          */
245         protected void appendSimpleContents(CharArrayBuffer buffer) {
246                 // append eveything before my name
247                 buffer.append(fDocument, fSourceRange[0], fNameRange[0]
248                                 - fSourceRange[0]);
249                 // append my name
250                 buffer.append(fName);
251                 // append everything after my name
252                 buffer.append(fDocument, fNameRange[1] + 1, fSourceRange[1]
253                                 - fNameRange[1]);
254         }
255
256         /**
257          * Generates detailed source indexes for this node if possible.
258          * 
259          * @exception DOMException
260          *                if unable to generate detailed source indexes for this
261          *                node
262          */
263         // protected void becomeDetailed() throws DOMException {
264         // if (!isDetailed()) {
265         // if (isVariableDeclarator() || hasMultipleVariableDeclarators()) {
266         // DOMNode first = getFirstFieldDeclaration();
267         // DOMNode last = getLastFieldDeclaration();
268         // DOMNode node= first;
269         // String source= first.getContents();
270         // while (node != last) {
271         // node= node.fNextNode;
272         // source+=node.getContents();
273         // }
274         // DOMBuilder builder = new DOMBuilder();
275         // IDOMField[] details= builder.createFields(source.toCharArray());
276         // if (details.length == 0) {
277         // throw new DOMException(ProjectPrefUtil.bind("dom.cannotDetail"));
278         // //$NON-NLS-1$
279         // } else {
280         // node= this;
281         // for (int i= 0; i < details.length; i++) {
282         // node.shareContents((DOMNode)details[i]);
283         // node= node.fNextNode;
284         // }
285         // }
286         // } else {
287         // super.becomeDetailed();
288         // }
289         //
290         // }
291         // }
292         /**
293          * @see IDOMNode#clone()
294          */
295         public Object clone() {
296                 // if (isVariableDeclarator() || hasMultipleVariableDeclarators()) {
297                 // return getFactory().createField(new
298                 // String(getSingleVariableDeclaratorContents()));
299                 // } else {
300                 return super.clone();
301                 // }
302         }
303
304         /**
305          * Expands all variable declarators in this field declaration into
306          * stand-alone field declarations.
307          */
308         protected void expand() {
309                 if (isVariableDeclarator() || hasMultipleVariableDeclarators()) {
310                         Enumeration siblings = new SiblingEnumeration(
311                                         getFirstFieldDeclaration());
312                         DOMField field = (DOMField) siblings.nextElement();
313                         DOMNode next = field.fNextNode;
314                         while (siblings.hasMoreElements() && (next instanceof DOMField)
315                                         && (((DOMField) next).isVariableDeclarator())) {
316                                 field.localizeContents();
317                                 if (field.fParent != null) {
318                                         field.fParent.fragment();
319                                 }
320                                 field = (DOMField) siblings.nextElement();
321                                 next = field.fNextNode;
322                         }
323                         field.localizeContents();
324                 }
325         }
326
327         /**
328          * @see DOMNode#getDetailedNode()
329          */
330         // protected DOMNode getDetailedNode() {
331         // if (isVariableDeclarator() || hasMultipleVariableDeclarators()) {
332         // return (DOMNode)getFactory().createField(new
333         // String(getSingleVariableDeclaratorContents()));
334         // } else {
335         // return (DOMNode)getFactory().createField(getContents());
336         // }
337         // }
338         /**
339          * Returns the first field document fragment that defines the type for this
340          * variable declarator.
341          */
342         protected DOMField getFirstFieldDeclaration() {
343                 if (isVariableDeclarator()) {
344                         return ((DOMField) fPreviousNode).getFirstFieldDeclaration();
345                 } else {
346                         return this;
347                 }
348         }
349
350         /**
351          * @see IDOMField#getInitializer()
352          */
353         public String getInitializer() {
354                 becomeDetailed();
355                 if (hasInitializer()) {
356                         if (fInitializer != null) {
357                                 return fInitializer;
358                         } else {
359                                 return CharArrayOps.substring(fDocument, fInitializerRange[0],
360                                                 fInitializerRange[1] + 1 - fInitializerRange[0]);
361                         }
362                 } else {
363                         return null;
364                 }
365         }
366
367         /**
368          * @see IDOMNode#getJavaElement
369          */
370         public IJavaElement getJavaElement(IJavaElement parent)
371                         throws IllegalArgumentException {
372                 if (parent.getElementType() == IJavaElement.TYPE) {
373                         return ((IType) parent).getField(getName());
374                 } else {
375                         throw new IllegalArgumentException(Util
376                                         .bind("element.illegalParent")); //$NON-NLS-1$
377                 }
378         }
379
380         /**
381          * Returns the last field document fragment in this muli-declarator
382          * statement.
383          */
384         protected DOMField getLastFieldDeclaration() {
385                 DOMField field = this;
386                 while (field.isVariableDeclarator()
387                                 || field.hasMultipleVariableDeclarators()) {
388                         if (field.fNextNode instanceof DOMField
389                                         && ((DOMField) field.fNextNode).isVariableDeclarator()) {
390                                 field = (DOMField) field.fNextNode;
391                         } else {
392                                 break;
393                         }
394                 }
395                 return field;
396         }
397
398         /**
399          * @see DOMMember#getMemberDeclarationStartPosition()
400          */
401         protected int getMemberDeclarationStartPosition() {
402                 return fTypeRange[0];
403         }
404
405         /**
406          * @see IDOMNode#getNodeType()
407          */
408         public int getNodeType() {
409                 return IDOMNode.FIELD;
410         }
411
412         /**
413          * Returns a String representing this field declaration as a field
414          * declaration with one variable declarator.
415          */
416         protected char[] getSingleVariableDeclaratorContents() {
417
418                 CharArrayBuffer buffer = new CharArrayBuffer();
419                 DOMField first = getFirstFieldDeclaration();
420                 if (first.isDetailed()) {
421                         first.appendMemberHeaderFragment(buffer);
422                         buffer.append(getType());
423                         if (isVariableDeclarator()) {
424                                 buffer.append(' ');
425                         } else {
426                                 buffer.append(fDocument, fTypeRange[1] + 1, fNameRange[0]
427                                                 - fTypeRange[1] - 1);
428                         }
429                 } else {
430                         buffer.append(first.fDocument, first.fSourceRange[0],
431                                         first.fNameRange[0] - first.fSourceRange[0]);
432                 }
433
434                 buffer.append(getName());
435                 if (hasInitializer()) {
436                         if (fInitializerRange[0] < 0) {
437                                 buffer.append('=').append(fInitializer).append(';').append(
438                                                 Util.LINE_SEPARATOR);
439                         } else {
440                                 buffer.append(fDocument, fNameRange[1] + 1,
441                                                 fInitializerRange[0] - fNameRange[1] - 1).append(
442                                                 getInitializer()).append(';').append(
443                                                 Util.LINE_SEPARATOR);
444                         }
445                 } else {
446                         buffer.append(';').append(Util.LINE_SEPARATOR);
447                 }
448                 return buffer.getContents();
449         }
450
451         /**
452          * @see IDOMField#getType()
453          */
454         public String getType() {
455                 return fType;
456         }
457
458         /**
459          * Returns the souce code to be used for this field's type.
460          */
461         protected char[] getTypeContents() {
462                 if (isTypeAltered()) {
463                         return fType.toCharArray();
464                 } else {
465                         return CharArrayOps.subarray(fDocument, fTypeRange[0],
466                                         fTypeRange[1] + 1 - fTypeRange[0]);
467                 }
468         }
469
470         /**
471          * Returns true if this field has an initializer expression, otherwise
472          * false.
473          */
474         protected boolean hasInitializer() {
475                 return getMask(MASK_FIELD_HAS_INITIALIZER);
476         }
477
478         /**
479          * Returns true is this field declarations has more than one variable
480          * declarator, otherwise false;
481          */
482         protected boolean hasMultipleVariableDeclarators() {
483                 return fNextNode != null && (fNextNode instanceof DOMField)
484                                 && ((DOMField) fNextNode).isVariableDeclarator();
485         }
486
487         /**
488          * Inserts the given un-parented node as a sibling of this node, immediately
489          * before this node. Once inserted, the sibling is only dependent on this
490          * document fragment.
491          * 
492          * <p>
493          * When a sibling is inserted before a variable declarator, it must first be
494          * expanded.
495          * 
496          * @see IDOMNode#insertSibling(IDOMNode)
497          */
498         public void insertSibling(IDOMNode sibling)
499                         throws IllegalArgumentException, DOMException {
500                 if (isVariableDeclarator()) {
501                         expand();
502                 }
503                 super.insertSibling(sibling);
504         }
505
506         /**
507          * Returns true if this field's type has been altered from the original
508          * document contents.
509          */
510         protected boolean isTypeAltered() {
511                 return getMask(MASK_FIELD_TYPE_ALTERED);
512         }
513
514         /**
515          * Returns true if this field is declared as a secondary variable declarator
516          * for a previous field declaration.
517          */
518         protected boolean isVariableDeclarator() {
519                 return getMask(MASK_FIELD_IS_VARIABLE_DECLARATOR);
520         }
521
522         /**
523          * @see DOMNode
524          */
525         protected DOMNode newDOMNode() {
526                 return new DOMField();
527         }
528
529         /**
530          * Normalizes this <code>DOMNode</code>'s end position.
531          */
532         void normalizeEndPosition(ILineStartFinder finder, DOMNode next) {
533                 if (next == null) {
534                         // this node's end position includes all of the characters up
535                         // to the end of the enclosing node
536                         DOMNode parent = (DOMNode) getParent();
537                         if (parent == null || parent instanceof DOMCompilationUnit) {
538                                 setSourceRangeEnd(fDocument.length - 1);
539                         } else {
540                                 // parent is a type
541                                 int temp = ((DOMType) parent).getCloseBodyPosition() - 1;
542                                 setSourceRangeEnd(temp);
543                                 fInsertionPosition = Math.max(finder.getLineStart(temp + 1),
544                                                 getEndPosition());
545                         }
546                 } else {
547                         // this node's end position is just before the start of the next
548                         // node
549                         // unless the next node is a field that is declared along with this
550                         // one
551                         int temp = next.getStartPosition() - 1;
552                         fInsertionPosition = Math.max(finder.getLineStart(temp + 1),
553                                         getEndPosition());
554
555                         next.normalizeStartPosition(getEndPosition(), finder);
556                         if (next instanceof DOMField) {
557                                 DOMField field = (DOMField) next;
558                                 if (field.isVariableDeclarator()
559                                                 && fTypeRange[0] == field.fTypeRange[0])
560                                         return;
561                         }
562                         setSourceRangeEnd(next.getStartPosition() - 1);
563                 }
564         }
565
566         /**
567          * Normalizes this <code>DOMNode</code>'s start position.
568          */
569         void normalizeStartPosition(int endPosition, ILineStartFinder finder) {
570                 if (isVariableDeclarator()) {
571                         // start position is end of last element
572                         setStartPosition(fPreviousNode.getEndPosition() + 1);
573                 } else {
574                         super.normalizeStartPosition(endPosition, finder);
575                 }
576         }
577
578         /**
579          * Offsets all the source indexes in this node by the given amount.
580          */
581         protected void offset(int offset) {
582                 super.offset(offset);
583                 offsetRange(fInitializerRange, offset);
584                 offsetRange(fTypeRange, offset);
585         }
586
587         /**
588          * Separates this node from its parent and siblings, maintaining any ties
589          * that this node has to the underlying document fragment.
590          * 
591          * <p>
592          * When a field with multiple declarators is removed, its declaration must
593          * first be expanded.
594          * 
595          * @see IDOMNode#remove()
596          */
597         public void remove() {
598                 expand();
599                 super.remove();
600         }
601
602         /**
603          * @see IDOMMember#setComment(String)
604          */
605         public void setComment(String comment) {
606                 expand();
607                 super.setComment(comment);
608         }
609
610         /**
611          * @see IDOMMember#setFlags(int)
612          */
613         public void setFlags(int flags) {
614                 expand();
615                 super.setFlags(flags);
616         }
617
618         /**
619          * Sets the state of this field declaration as having an initializer
620          * expression.
621          */
622         protected void setHasInitializer(boolean hasInitializer) {
623                 setMask(MASK_FIELD_HAS_INITIALIZER, hasInitializer);
624         }
625
626         /**
627          * @see IDOMField#setInitializer(char[])
628          */
629         public void setInitializer(String initializer) {
630                 becomeDetailed();
631                 fragment();
632                 setHasInitializer(initializer != null);
633                 fInitializer = initializer;
634         }
635
636         /**
637          * Sets the initializer range.
638          */
639         void setInitializerRange(int start, int end) {
640                 fInitializerRange[0] = start;
641                 fInitializerRange[1] = end;
642         }
643
644         /**
645          * Sets the state of this field declaration as being a secondary variable
646          * declarator for a previous field declaration.
647          */
648         protected void setIsVariableDeclarator(boolean isVariableDeclarator) {
649                 setMask(MASK_FIELD_IS_VARIABLE_DECLARATOR, isVariableDeclarator);
650         }
651
652         /**
653          * @see IDOMField#setName(char[])
654          */
655         public void setName(String name) throws IllegalArgumentException {
656                 if (name == null) {
657                         throw new IllegalArgumentException(Util.bind("element.nullName")); //$NON-NLS-1$
658                 } else {
659                         super.setName(name);
660                         setTypeAltered(true);
661                 }
662         }
663
664         /**
665          * @see IDOMField#setType(char[])
666          */
667         public void setType(String typeName) throws IllegalArgumentException {
668                 if (typeName == null) {
669                         throw new IllegalArgumentException(Util.bind("element.nullType")); //$NON-NLS-1$
670                 }
671                 becomeDetailed();
672                 expand();
673                 fragment();
674                 setTypeAltered(true);
675                 setNameAltered(true);
676                 fType = typeName;
677         }
678
679         /**
680          * Sets the state of this field declaration as having the field type altered
681          * from the original document.
682          */
683         protected void setTypeAltered(boolean typeAltered) {
684                 setMask(MASK_FIELD_TYPE_ALTERED, typeAltered);
685         }
686
687         /**
688          * @see DOMNode#shareContents(DOMNode)
689          */
690         protected void shareContents(DOMNode node) {
691                 super.shareContents(node);
692                 DOMField field = (DOMField) node;
693                 fInitializer = field.fInitializer;
694                 fInitializerRange = rangeCopy(field.fInitializerRange);
695                 fType = field.fType;
696                 fTypeRange = rangeCopy(field.fTypeRange);
697         }
698
699         /**
700          * @see IDOMNode#toString()
701          */
702         public String toString() {
703                 return "FIELD: " + getName(); //$NON-NLS-1$
704         }
705 }