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