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
35 * has been altered from the contents in the
36 * document, otherwise <code>null</code>.
38 protected String fType;
41 * The original inclusive source range of the
42 * field's type in the document.
44 protected int[] fTypeRange;
47 * The contents of the initializer when the
48 * initializer has been altered from the
49 * original state in the document, otherwise
52 protected String fInitializer;
55 * The original inclusive source range of the
56 * initializer in the document.
58 protected int[] fInitializerRange;
61 * Constructs an empty field node.
66 * Creates a new detailed FIELD document fragment on the given range of the document.
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
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
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
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,
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
106 * @param isVariableDeclarator - true if the field is a seconday variable declarator
107 * for a previous field declaration.
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);
113 fTypeRange= typeRange;
114 setHasInitializer(hasInitializer);
115 fInitializerRange= initRange;
116 setIsVariableDeclarator(isVariableDeclarator);
117 setMask(MASK_DETAILED_SOURCE_INDEXES, true);
121 * Creates a new simple FIELD document fragment on the given range of the document.
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
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.
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);
147 * Appends this member's body contents to the given CharArrayBuffer.
148 * Body contents include the member body and any trailing whitespace.
150 * <p>A field does not have a body.
152 * @see DOMMember#appendMemberBodyContents(CharArrayBuffer)
154 protected void appendMemberBodyContents(CharArrayBuffer buffer) {}
156 * @see DOMMember#appendMemberDeclarationContents(CharArrayBuffer)
158 protected void appendMemberDeclarationContents(CharArrayBuffer buffer) {
160 if (isVariableDeclarator()) {
161 buffer.append(fDocument, fSourceRange[0], fNameRange[0] - fSourceRange[0]);
164 .append(getTypeContents())
165 .append(fDocument, fTypeRange[1] + 1, fNameRange[0] - fTypeRange[1] - 1);
168 buffer.append(getNameContents());
169 if (hasInitializer()) {
170 if (fInitializerRange[0] < 0) {
173 .append(fInitializer)
174 .append(fDocument, fNameRange[1] + 1, fSourceRange[1] - fNameRange[1]);
177 .append(fDocument, fNameRange[1] + 1, fInitializerRange[0] - fNameRange[1] - 1)
178 .append(getInitializer())
179 .append(fDocument, fInitializerRange[1] + 1, fSourceRange[1] - fInitializerRange[1]);
182 if (fInitializerRange[0] < 0) {
183 buffer.append(fDocument, fNameRange[1] + 1, fSourceRange[1] - fNameRange[1]);
185 buffer.append(fDocument, fInitializerRange[1] + 1, fSourceRange[1] - fInitializerRange[1]);
191 * Appends this member's header contents to the given CharArrayBuffer.
192 * Header contents include any preceding comments and modifiers.
194 * <p>If this field is a secondary variable declarator, there is no header.
196 * @see DOMMember#appendMemberHeaderFragment(CharArrayBuffer)
198 protected void appendMemberHeaderFragment(CharArrayBuffer buffer) {
200 if (isVariableDeclarator()) {
203 super.appendMemberHeaderFragment(buffer);
208 * @see DOMMember#appendSimpleContents(CharArrayBuffer)
210 protected void appendSimpleContents(CharArrayBuffer buffer) {
211 // append eveything before my name
212 buffer.append(fDocument, fSourceRange[0], fNameRange[0] - fSourceRange[0]);
214 buffer.append(fName);
215 // append everything after my name
216 buffer.append(fDocument, fNameRange[1] + 1, fSourceRange[1] - fNameRange[1]);
219 * Generates detailed source indexes for this node if possible.
221 * @exception DOMException if unable to generate detailed source indexes
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();
235 // DOMBuilder builder = new DOMBuilder();
236 // IDOMField[] details= builder.createFields(source.toCharArray());
237 // if (details.length == 0) {
238 // throw new DOMException(ProjectPrefUtil.bind("dom.cannotDetail")); //$NON-NLS-1$
241 // for (int i= 0; i < details.length; i++) {
242 // node.shareContents((DOMNode)details[i]);
243 // node= node.fNextNode;
247 // super.becomeDetailed();
253 * @see IDOMNode#clone()
255 public Object clone() {
256 // if (isVariableDeclarator() || hasMultipleVariableDeclarators()) {
257 // return getFactory().createField(new String(getSingleVariableDeclaratorContents()));
259 return super.clone();
263 * Expands all variable declarators in this field declaration into
264 * stand-alone field declarations.
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();
276 field= (DOMField)siblings.nextElement();
277 next= field.fNextNode;
279 field.localizeContents();
283 * @see DOMNode#getDetailedNode()
285 //protected DOMNode getDetailedNode() {
286 // if (isVariableDeclarator() || hasMultipleVariableDeclarators()) {
287 // return (DOMNode)getFactory().createField(new String(getSingleVariableDeclaratorContents()));
289 // return (DOMNode)getFactory().createField(getContents());
293 * Returns the first field document fragment that defines
294 * the type for this variable declarator.
296 protected DOMField getFirstFieldDeclaration() {
297 if (isVariableDeclarator()) {
298 return ((DOMField)fPreviousNode).getFirstFieldDeclaration();
304 * @see IDOMField#getInitializer()
306 public String getInitializer() {
308 if (hasInitializer()) {
309 if (fInitializer != null) {
312 return CharArrayOps.substring(fDocument, fInitializerRange[0], fInitializerRange[1] + 1 - fInitializerRange[0]);
319 * @see IDOMNode#getJavaElement
321 public IJavaElement getJavaElement(IJavaElement parent) throws IllegalArgumentException {
322 if (parent.getElementType() == IJavaElement.TYPE) {
323 return ((IType)parent).getField(getName());
325 throw new IllegalArgumentException(Util.bind("element.illegalParent")); //$NON-NLS-1$
329 * Returns the last field document fragment in this muli-declarator statement.
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;
343 * @see DOMMember#getMemberDeclarationStartPosition()
345 protected int getMemberDeclarationStartPosition() {
346 return fTypeRange[0];
349 * @see IDOMNode#getNodeType()
351 public int getNodeType() {
352 return IDOMNode.FIELD;
355 * Returns a String representing this field declaration as a field
356 * declaration with one variable declarator.
358 protected char[] getSingleVariableDeclaratorContents() {
361 CharArrayBuffer buffer= new CharArrayBuffer();
362 DOMField first= getFirstFieldDeclaration();
363 if (first.isDetailed()) {
364 first.appendMemberHeaderFragment(buffer);
365 buffer.append(getType());
366 if (isVariableDeclarator()) {
369 buffer.append(fDocument, fTypeRange[1] + 1, fNameRange[0] - fTypeRange[1] - 1);
372 buffer.append(first.fDocument, first.fSourceRange[0], first.fNameRange[0] - first.fSourceRange[0]);
375 buffer.append(getName());
376 if (hasInitializer()) {
377 if (fInitializerRange[0] < 0) {
380 .append(fInitializer)
382 .append(Util.LINE_SEPARATOR);
385 .append(fDocument, fNameRange[1] + 1, fInitializerRange[0] - fNameRange[1] - 1)
386 .append(getInitializer())
388 .append(Util.LINE_SEPARATOR);
391 buffer.append(';').append(Util.LINE_SEPARATOR);
393 return buffer.getContents();
396 * @see IDOMField#getType()
398 public String getType() {
402 * Returns the souce code to be used for this
405 protected char[] getTypeContents() {
406 if (isTypeAltered()) {
407 return fType.toCharArray();
409 return CharArrayOps.subarray(fDocument, fTypeRange[0], fTypeRange[1] + 1 - fTypeRange[0]);
413 * Returns true if this field has an initializer expression,
416 protected boolean hasInitializer() {
417 return getMask(MASK_FIELD_HAS_INITIALIZER);
420 * Returns true is this field declarations has more than one
421 * variable declarator, otherwise false;
423 protected boolean hasMultipleVariableDeclarators() {
424 return fNextNode != null && (fNextNode instanceof DOMField) &&
425 ((DOMField)fNextNode).isVariableDeclarator();
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.
431 * <p>When a sibling is inserted before a variable declarator, it must first
434 * @see IDOMNode#insertSibling(IDOMNode)
436 public void insertSibling(IDOMNode sibling) throws IllegalArgumentException, DOMException {
437 if (isVariableDeclarator()) {
440 super.insertSibling(sibling);
443 * Returns true if this field's type has been altered
444 * from the original document contents.
446 protected boolean isTypeAltered() {
447 return getMask(MASK_FIELD_TYPE_ALTERED);
450 * Returns true if this field is declared as a secondary variable
451 * declarator for a previous field declaration.
453 protected boolean isVariableDeclarator() {
454 return getMask(MASK_FIELD_IS_VARIABLE_DECLARATOR);
459 protected DOMNode newDOMNode() {
460 return new DOMField();
463 * Normalizes this <code>DOMNode</code>'s end position.
465 void normalizeEndPosition(ILineStartFinder finder, DOMNode next) {
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);
474 int temp = ((DOMType)parent).getCloseBodyPosition() - 1;
475 setSourceRangeEnd(temp);
476 fInsertionPosition = Math.max(finder.getLineStart(temp + 1), getEndPosition());
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());
484 next.normalizeStartPosition(getEndPosition(), finder);
485 if (next instanceof DOMField) {
486 DOMField field = (DOMField) next;
487 if (field.isVariableDeclarator() && fTypeRange[0] == field.fTypeRange[0])
490 setSourceRangeEnd(next.getStartPosition() - 1);
494 * Normalizes this <code>DOMNode</code>'s start position.
496 void normalizeStartPosition(int endPosition, ILineStartFinder finder) {
497 if (isVariableDeclarator()) {
498 // start position is end of last element
499 setStartPosition(fPreviousNode.getEndPosition() + 1);
501 super.normalizeStartPosition(endPosition, finder);
505 * Offsets all the source indexes in this node by the given amount.
507 protected void offset(int offset) {
508 super.offset(offset);
509 offsetRange(fInitializerRange, offset);
510 offsetRange(fTypeRange, offset);
513 * Separates this node from its parent and siblings, maintaining any ties that
514 * this node has to the underlying document fragment.
516 * <p>When a field with multiple declarators is removed, its declaration
517 * must first be expanded.
519 * @see IDOMNode#remove()
521 public void remove() {
526 * @see IDOMMember#setComment(String)
528 public void setComment(String comment) {
530 super.setComment(comment);
533 * @see IDOMMember#setFlags(int)
535 public void setFlags(int flags) {
537 super.setFlags(flags);
540 * Sets the state of this field declaration as having
541 * an initializer expression.
543 protected void setHasInitializer(boolean hasInitializer) {
544 setMask(MASK_FIELD_HAS_INITIALIZER, hasInitializer);
547 * @see IDOMField#setInitializer(char[])
549 public void setInitializer(String initializer) {
552 setHasInitializer(initializer != null);
553 fInitializer= initializer;
556 * Sets the initializer range.
558 void setInitializerRange(int start, int end) {
559 fInitializerRange[0] = start;
560 fInitializerRange[1] = end;
563 * Sets the state of this field declaration as being a
564 * secondary variable declarator for a previous field
567 protected void setIsVariableDeclarator(boolean isVariableDeclarator) {
568 setMask(MASK_FIELD_IS_VARIABLE_DECLARATOR, isVariableDeclarator);
571 * @see IDOMField#setName(char[])
573 public void setName(String name) throws IllegalArgumentException {
575 throw new IllegalArgumentException(Util.bind("element.nullName")); //$NON-NLS-1$
578 setTypeAltered(true);
582 * @see IDOMField#setType(char[])
584 public void setType(String typeName) throws IllegalArgumentException {
585 if (typeName == null) {
586 throw new IllegalArgumentException(Util.bind("element.nullType")); //$NON-NLS-1$
591 setTypeAltered(true);
592 setNameAltered(true);
596 * Sets the state of this field declaration as having
597 * the field type altered from the original document.
599 protected void setTypeAltered(boolean typeAltered) {
600 setMask(MASK_FIELD_TYPE_ALTERED, typeAltered);
603 * @see DOMNode#shareContents(DOMNode)
605 protected void shareContents(DOMNode node) {
606 super.shareContents(node);
607 DOMField field= (DOMField)node;
608 fInitializer= field.fInitializer;
609 fInitializerRange= rangeCopy(field.fInitializerRange);
611 fTypeRange= rangeCopy(field.fTypeRange);
614 * @see IDOMNode#toString()
616 public String toString() {
617 return "FIELD: " + getName(); //$NON-NLS-1$