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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package net.sourceforge.phpdt.internal.core.jdom;
13 import java.util.Enumeration;
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;
26 * DOMField provides an implementation of IDOMField.
31 class DOMField extends DOMMember implements IDOMField {
34 * Contains the type of the field when the type has been altered from the
35 * contents in the document, otherwise <code>null</code>.
37 protected String fType;
40 * The original inclusive source range of the field's type in the document.
42 protected int[] fTypeRange;
45 * The contents of the initializer when the initializer has been altered
46 * from the original state in the document, otherwise <code>null</code>.
48 protected String fInitializer;
51 * The original inclusive source range of the initializer in the document.
53 protected int[] fInitializerRange;
56 * Constructs an empty field node.
62 * Creates a new detailed FIELD document fragment on the given range of the
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.
75 * the identifier portion of the name of this field,
76 * corresponding to VariableDeclaratorId (JLS 8.3).
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
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.
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.
100 * a two element array describing the location of the typeName in
101 * the document - the positions of the first and last characters
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,
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.
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,
127 fTypeRange = typeRange;
128 setHasInitializer(hasInitializer);
129 fInitializerRange = initRange;
130 setIsVariableDeclarator(isVariableDeclarator);
131 setMask(MASK_DETAILED_SOURCE_INDEXES, true);
136 * Creates a new simple FIELD document fragment on the given range of the
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.
149 * the identifier portion of the name of this field,
150 * corresponding to VariableDeclaratorId (JLS 8.3).
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
157 * an integer representing the modifiers for this member. The
158 * integer can be analyzed with net.sourceforge.phpdt.core.Flags
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.
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);
175 * Appends this member's body contents to the given CharArrayBuffer. Body
176 * contents include the member body and any trailing whitespace.
179 * A field does not have a body.
181 * @see DOMMember#appendMemberBodyContents(CharArrayBuffer)
183 protected void appendMemberBodyContents(CharArrayBuffer buffer) {
187 * @see DOMMember#appendMemberDeclarationContents(CharArrayBuffer)
189 protected void appendMemberDeclarationContents(CharArrayBuffer buffer) {
191 if (isVariableDeclarator()) {
192 buffer.append(fDocument, fSourceRange[0], fNameRange[0]
195 buffer.append(getTypeContents()).append(fDocument,
196 fTypeRange[1] + 1, fNameRange[0] - fTypeRange[1] - 1);
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]);
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]);
212 if (fInitializerRange[0] < 0) {
213 buffer.append(fDocument, fNameRange[1] + 1, fSourceRange[1]
216 buffer.append(fDocument, fInitializerRange[1] + 1,
217 fSourceRange[1] - fInitializerRange[1]);
224 * Appends this member's header contents to the given CharArrayBuffer.
225 * Header contents include any preceding comments and modifiers.
228 * If this field is a secondary variable declarator, there is no header.
230 * @see DOMMember#appendMemberHeaderFragment(CharArrayBuffer)
232 protected void appendMemberHeaderFragment(CharArrayBuffer buffer) {
234 if (isVariableDeclarator()) {
237 super.appendMemberHeaderFragment(buffer);
243 * @see DOMMember#appendSimpleContents(CharArrayBuffer)
245 protected void appendSimpleContents(CharArrayBuffer buffer) {
246 // append eveything before my name
247 buffer.append(fDocument, fSourceRange[0], fNameRange[0]
250 buffer.append(fName);
251 // append everything after my name
252 buffer.append(fDocument, fNameRange[1] + 1, fSourceRange[1]
257 * Generates detailed source indexes for this node if possible.
259 * @exception DOMException
260 * if unable to generate detailed source indexes for this
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();
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"));
281 // for (int i= 0; i < details.length; i++) {
282 // node.shareContents((DOMNode)details[i]);
283 // node= node.fNextNode;
287 // super.becomeDetailed();
293 * @see IDOMNode#clone()
295 public Object clone() {
296 // if (isVariableDeclarator() || hasMultipleVariableDeclarators()) {
297 // return getFactory().createField(new
298 // String(getSingleVariableDeclaratorContents()));
300 return super.clone();
305 * Expands all variable declarators in this field declaration into
306 * stand-alone field declarations.
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();
320 field = (DOMField) siblings.nextElement();
321 next = field.fNextNode;
323 field.localizeContents();
328 * @see DOMNode#getDetailedNode()
330 // protected DOMNode getDetailedNode() {
331 // if (isVariableDeclarator() || hasMultipleVariableDeclarators()) {
332 // return (DOMNode)getFactory().createField(new
333 // String(getSingleVariableDeclaratorContents()));
335 // return (DOMNode)getFactory().createField(getContents());
339 * Returns the first field document fragment that defines the type for this
340 * variable declarator.
342 protected DOMField getFirstFieldDeclaration() {
343 if (isVariableDeclarator()) {
344 return ((DOMField) fPreviousNode).getFirstFieldDeclaration();
351 * @see IDOMField#getInitializer()
353 public String getInitializer() {
355 if (hasInitializer()) {
356 if (fInitializer != null) {
359 return CharArrayOps.substring(fDocument, fInitializerRange[0],
360 fInitializerRange[1] + 1 - fInitializerRange[0]);
368 * @see IDOMNode#getJavaElement
370 public IJavaElement getJavaElement(IJavaElement parent)
371 throws IllegalArgumentException {
372 if (parent.getElementType() == IJavaElement.TYPE) {
373 return ((IType) parent).getField(getName());
375 throw new IllegalArgumentException(Util
376 .bind("element.illegalParent")); //$NON-NLS-1$
381 * Returns the last field document fragment in this muli-declarator
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;
399 * @see DOMMember#getMemberDeclarationStartPosition()
401 protected int getMemberDeclarationStartPosition() {
402 return fTypeRange[0];
406 * @see IDOMNode#getNodeType()
408 public int getNodeType() {
409 return IDOMNode.FIELD;
413 * Returns a String representing this field declaration as a field
414 * declaration with one variable declarator.
416 protected char[] getSingleVariableDeclaratorContents() {
418 CharArrayBuffer buffer = new CharArrayBuffer();
419 DOMField first = getFirstFieldDeclaration();
420 if (first.isDetailed()) {
421 first.appendMemberHeaderFragment(buffer);
422 buffer.append(getType());
423 if (isVariableDeclarator()) {
426 buffer.append(fDocument, fTypeRange[1] + 1, fNameRange[0]
427 - fTypeRange[1] - 1);
430 buffer.append(first.fDocument, first.fSourceRange[0],
431 first.fNameRange[0] - first.fSourceRange[0]);
434 buffer.append(getName());
435 if (hasInitializer()) {
436 if (fInitializerRange[0] < 0) {
437 buffer.append('=').append(fInitializer).append(';').append(
438 Util.LINE_SEPARATOR);
440 buffer.append(fDocument, fNameRange[1] + 1,
441 fInitializerRange[0] - fNameRange[1] - 1).append(
442 getInitializer()).append(';').append(
443 Util.LINE_SEPARATOR);
446 buffer.append(';').append(Util.LINE_SEPARATOR);
448 return buffer.getContents();
452 * @see IDOMField#getType()
454 public String getType() {
459 * Returns the souce code to be used for this field's type.
461 protected char[] getTypeContents() {
462 if (isTypeAltered()) {
463 return fType.toCharArray();
465 return CharArrayOps.subarray(fDocument, fTypeRange[0],
466 fTypeRange[1] + 1 - fTypeRange[0]);
471 * Returns true if this field has an initializer expression, otherwise
474 protected boolean hasInitializer() {
475 return getMask(MASK_FIELD_HAS_INITIALIZER);
479 * Returns true is this field declarations has more than one variable
480 * declarator, otherwise false;
482 protected boolean hasMultipleVariableDeclarators() {
483 return fNextNode != null && (fNextNode instanceof DOMField)
484 && ((DOMField) fNextNode).isVariableDeclarator();
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
493 * When a sibling is inserted before a variable declarator, it must first be
496 * @see IDOMNode#insertSibling(IDOMNode)
498 public void insertSibling(IDOMNode sibling)
499 throws IllegalArgumentException, DOMException {
500 if (isVariableDeclarator()) {
503 super.insertSibling(sibling);
507 * Returns true if this field's type has been altered from the original
510 protected boolean isTypeAltered() {
511 return getMask(MASK_FIELD_TYPE_ALTERED);
515 * Returns true if this field is declared as a secondary variable declarator
516 * for a previous field declaration.
518 protected boolean isVariableDeclarator() {
519 return getMask(MASK_FIELD_IS_VARIABLE_DECLARATOR);
525 protected DOMNode newDOMNode() {
526 return new DOMField();
530 * Normalizes this <code>DOMNode</code>'s end position.
532 void normalizeEndPosition(ILineStartFinder finder, DOMNode next) {
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);
541 int temp = ((DOMType) parent).getCloseBodyPosition() - 1;
542 setSourceRangeEnd(temp);
543 fInsertionPosition = Math.max(finder.getLineStart(temp + 1),
547 // this node's end position is just before the start of the next
549 // unless the next node is a field that is declared along with this
551 int temp = next.getStartPosition() - 1;
552 fInsertionPosition = Math.max(finder.getLineStart(temp + 1),
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])
562 setSourceRangeEnd(next.getStartPosition() - 1);
567 * Normalizes this <code>DOMNode</code>'s start position.
569 void normalizeStartPosition(int endPosition, ILineStartFinder finder) {
570 if (isVariableDeclarator()) {
571 // start position is end of last element
572 setStartPosition(fPreviousNode.getEndPosition() + 1);
574 super.normalizeStartPosition(endPosition, finder);
579 * Offsets all the source indexes in this node by the given amount.
581 protected void offset(int offset) {
582 super.offset(offset);
583 offsetRange(fInitializerRange, offset);
584 offsetRange(fTypeRange, offset);
588 * Separates this node from its parent and siblings, maintaining any ties
589 * that this node has to the underlying document fragment.
592 * When a field with multiple declarators is removed, its declaration must
595 * @see IDOMNode#remove()
597 public void remove() {
603 * @see IDOMMember#setComment(String)
605 public void setComment(String comment) {
607 super.setComment(comment);
611 * @see IDOMMember#setFlags(int)
613 public void setFlags(int flags) {
615 super.setFlags(flags);
619 * Sets the state of this field declaration as having an initializer
622 protected void setHasInitializer(boolean hasInitializer) {
623 setMask(MASK_FIELD_HAS_INITIALIZER, hasInitializer);
627 * @see IDOMField#setInitializer(char[])
629 public void setInitializer(String initializer) {
632 setHasInitializer(initializer != null);
633 fInitializer = initializer;
637 * Sets the initializer range.
639 void setInitializerRange(int start, int end) {
640 fInitializerRange[0] = start;
641 fInitializerRange[1] = end;
645 * Sets the state of this field declaration as being a secondary variable
646 * declarator for a previous field declaration.
648 protected void setIsVariableDeclarator(boolean isVariableDeclarator) {
649 setMask(MASK_FIELD_IS_VARIABLE_DECLARATOR, isVariableDeclarator);
653 * @see IDOMField#setName(char[])
655 public void setName(String name) throws IllegalArgumentException {
657 throw new IllegalArgumentException(Util.bind("element.nullName")); //$NON-NLS-1$
660 setTypeAltered(true);
665 * @see IDOMField#setType(char[])
667 public void setType(String typeName) throws IllegalArgumentException {
668 if (typeName == null) {
669 throw new IllegalArgumentException(Util.bind("element.nullType")); //$NON-NLS-1$
674 setTypeAltered(true);
675 setNameAltered(true);
680 * Sets the state of this field declaration as having the field type altered
681 * from the original document.
683 protected void setTypeAltered(boolean typeAltered) {
684 setMask(MASK_FIELD_TYPE_ALTERED, typeAltered);
688 * @see DOMNode#shareContents(DOMNode)
690 protected void shareContents(DOMNode node) {
691 super.shareContents(node);
692 DOMField field = (DOMField) node;
693 fInitializer = field.fInitializer;
694 fInitializerRange = rangeCopy(field.fInitializerRange);
696 fTypeRange = rangeCopy(field.fTypeRange);
700 * @see IDOMNode#toString()
702 public String toString() {
703 return "FIELD: " + getName(); //$NON-NLS-1$