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