package org.eclipse.jdt.internal.core.jdom; | |
/* | |
* (c) Copyright IBM Corp. 2000, 2001. | |
* All Rights Reserved. | |
*/ | |
import java.util.Enumeration; | |
import org.eclipse.jdt.core.IJavaElement; | |
import org.eclipse.jdt.core.IType; | |
import org.eclipse.jdt.core.jdom.DOMException; | |
import org.eclipse.jdt.core.jdom.IDOMField; | |
import org.eclipse.jdt.core.jdom.IDOMNode; | |
import org.eclipse.jdt.internal.core.JavaModelManager; | |
import org.eclipse.jdt.internal.core.Util; | |
import org.eclipse.jdt.internal.core.util.CharArrayBuffer; | |
import org.eclipse.jdt.internal.core.util.CharArrayOps; | |
/** | |
* DOMField provides an implementation of IDOMField. | |
* | |
* @see IDOMField | |
* @see DOMNode | |
*/ | |
class DOMField extends DOMMember implements IDOMField { | |
/** | |
* Contains the type of the field when the type | |
* has been altered from the contents in the | |
* document, otherwise <code>null</code>. | |
*/ | |
protected String fType; | |
/** | |
* The original inclusive source range of the | |
* field's type in the document. | |
*/ | |
protected int[] fTypeRange; | |
/** | |
* The contents of the initializer when the | |
* initializer has been altered from the | |
* original state in the document, otherwise | |
* <code>null</code>. | |
*/ | |
protected String fInitializer; | |
/** | |
* The original inclusive source range of the | |
* initializer in the document. | |
*/ | |
protected int[] fInitializerRange; | |
/** | |
* Constructs an empty field node. | |
*/ | |
DOMField() { | |
} | |
/** | |
* Creates a new detailed FIELD document fragment on the given range of the document. | |
* | |
* @param document - the document containing this node's original contents | |
* @param sourceRange - a two element array of integers describing the | |
* entire inclusive source range of this node within its document. | |
* Contents start on and include the character at the first position. | |
* Contents end on and include the character at the last position. | |
* An array of -1's indicates this node's contents do not exist | |
* in the document. | |
* @param name - the identifier portion of the name of this field, | |
* corresponding to VariableDeclaratorId (JLS 8.3). | |
* @param nameRange - a two element array of integers describing the | |
* entire inclusive source range of this node's name within its document, | |
* including any array qualifiers that might follow the name. | |
* @param commentRange - a two element array describing the comments that precede | |
* the member declaration. The first matches the start of this node's | |
* sourceRange, and the second is the new-line or first non-whitespace | |
* character following the last comment. If no comments are present, | |
* this array contains two -1's. | |
* @param flags - an integer representing the modifiers for this member. The | |
* integer can be analyzed with org.eclipse.jdt.core.Flags | |
* @param modifierRange - a two element array describing the location of | |
* modifiers for this member within its source range. The first integer | |
* is the first character of the first modifier for this member, and | |
* the second integer is the last whitespace character preceeding the | |
* next part of this member declaration. If there are no modifiers present | |
* in this node's source code (i.e. default protection), this array | |
* contains two -1's. | |
* @param typeRange- a two element array describing the location of the | |
* typeName in the document - the positions of the first and last characters | |
* of the typeName. | |
* @param type - the type of the field, in normalized form, as defined in | |
* Type in Field Declaration (JLS 8.3) | |
* @param hasInitializer - true if this field declaration includes an initializer, | |
* otherwise false | |
* @param initRange - a two element array describing the location of the initializer | |
* in the declaration. The first integer is the position of the character | |
* following the equals sign, and the position of the last character is the last | |
* in the initializer. If this field has no initializer, this array contains | |
* two -1's. | |
* @param isVariableDeclarator - true if the field is a seconday variable declarator | |
* for a previous field declaration. | |
*/ | |
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) { | |
super(document, sourceRange, name, nameRange, commentRange, flags, modifierRange); | |
fType= type; | |
fTypeRange= typeRange; | |
setHasInitializer(hasInitializer); | |
fInitializerRange= initRange; | |
setIsVariableDeclarator(isVariableDeclarator); | |
setMask(MASK_DETAILED_SOURCE_INDEXES, true); | |
} | |
/** | |
* Creates a new simple FIELD document fragment on the given range of the document. | |
* | |
* @param document - the document containing this node's original contents | |
* @param sourceRange - a two element array of integers describing the | |
* entire inclusive source range of this node within its document. | |
* Contents start on and include the character at the first position. | |
* Contents end on and include the character at the last position. | |
* An array of -1's indicates this node's contents do not exist | |
* in the document. | |
* @param name - the identifier portion of the name of this field, | |
* corresponding to VariableDeclaratorId (JLS 8.3). | |
* @param nameRange - a two element array of integers describing the | |
* entire inclusive source range of this node's name within its document, | |
* including any array qualifiers that might follow the name. | |
* @param flags - an integer representing the modifiers for this member. The | |
* integer can be analyzed with org.eclipse.jdt.core.Flags | |
* @param type - the type of the field, in normalized form, as defined in | |
* Type in Field Declaration (JLS 8.3) | |
* @param isVariableDeclarator - true if the field is a seconday variable declarator | |
* for a previous field declaration. | |
*/ | |
DOMField(char[] document, int[] sourceRange, String name, int[] nameRange, int flags, String type, boolean isVariableDeclarator) { | |
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); | |
setMask(MASK_DETAILED_SOURCE_INDEXES, false); | |
} | |
/** | |
* Appends this member's body contents to the given CharArrayBuffer. | |
* Body contents include the member body and any trailing whitespace. | |
* | |
* <p>A field does not have a body. | |
* | |
* @see DOMMember#appendMemberBodyContents(CharArrayBuffer) | |
*/ | |
protected void appendMemberBodyContents(CharArrayBuffer buffer) {} | |
/** | |
* @see DOMMember#appendMemberDeclarationContents(CharArrayBuffer) | |
*/ | |
protected void appendMemberDeclarationContents(CharArrayBuffer buffer) { | |
if (isVariableDeclarator()) { | |
buffer.append(fDocument, fSourceRange[0], fNameRange[0] - fSourceRange[0]); | |
} else { | |
buffer | |
.append(getTypeContents()) | |
.append(fDocument, fTypeRange[1] + 1, fNameRange[0] - fTypeRange[1] - 1); | |
} | |
buffer.append(getNameContents()); | |
if (hasInitializer()) { | |
if (fInitializerRange[0] < 0) { | |
buffer | |
.append('=') | |
.append(fInitializer) | |
.append(fDocument, fNameRange[1] + 1, fSourceRange[1] - fNameRange[1]); | |
} else { | |
buffer | |
.append(fDocument, fNameRange[1] + 1, fInitializerRange[0] - fNameRange[1] - 1) | |
.append(getInitializer()) | |
.append(fDocument, fInitializerRange[1] + 1, fSourceRange[1] - fInitializerRange[1]); | |
} | |
} else { | |
if (fInitializerRange[0] < 0) { | |
buffer.append(fDocument, fNameRange[1] + 1, fSourceRange[1] - fNameRange[1]); | |
} else { | |
buffer.append(fDocument, fInitializerRange[1] + 1, fSourceRange[1] - fInitializerRange[1]); | |
} | |
} | |
} | |
/** | |
* Appends this member's header contents to the given CharArrayBuffer. | |
* Header contents include any preceding comments and modifiers. | |
* | |
* <p>If this field is a secondary variable declarator, there is no header. | |
* | |
* @see DOMMember#appendMemberHeaderFragment(CharArrayBuffer) | |
*/ | |
protected void appendMemberHeaderFragment(CharArrayBuffer buffer) { | |
if (isVariableDeclarator()) { | |
return; | |
} else { | |
super.appendMemberHeaderFragment(buffer); | |
} | |
} | |
/** | |
* @see DOMMember#appendSimpleContents(CharArrayBuffer) | |
*/ | |
protected void appendSimpleContents(CharArrayBuffer buffer) { | |
// append eveything before my name | |
buffer.append(fDocument, fSourceRange[0], fNameRange[0] - fSourceRange[0]); | |
// append my name | |
buffer.append(fName); | |
// append everything after my name | |
buffer.append(fDocument, fNameRange[1] + 1, fSourceRange[1] - fNameRange[1]); | |
} | |
/** | |
* Generates detailed source indexes for this node if possible. | |
* | |
* @exception DOMException if unable to generate detailed source indexes | |
* for this node | |
*/ | |
protected void becomeDetailed() throws DOMException { | |
if (!isDetailed()) { | |
if (isVariableDeclarator() || hasMultipleVariableDeclarators()) { | |
DOMNode first = getFirstFieldDeclaration(); | |
DOMNode last = getLastFieldDeclaration(); | |
DOMNode node= first; | |
String source= first.getContents(); | |
while (node != last) { | |
node= node.fNextNode; | |
source+=node.getContents(); | |
} | |
DOMBuilder builder = new DOMBuilder(); | |
IDOMField[] details= builder.createFields(source.toCharArray()); | |
if (details.length == 0) { | |
throw new DOMException(Util.bind("dom.cannotDetail"/*nonNLS*/)); | |
} else { | |
node= this; | |
for (int i= 0; i < details.length; i++) { | |
node.shareContents((DOMNode)details[i]); | |
node= node.fNextNode; | |
} | |
} | |
} else { | |
super.becomeDetailed(); | |
} | |
} | |
} | |
/** | |
* @see IDOMNode#clone() | |
*/ | |
public Object clone() { | |
if (isVariableDeclarator() || hasMultipleVariableDeclarators()) { | |
return getFactory().createField(new String(getSingleVariableDeclaratorContents())); | |
} else { | |
return super.clone(); | |
} | |
} | |
/** | |
* Expands all variable declarators in this field declaration into | |
* stand-alone field declarations. | |
*/ | |
protected void expand() { | |
if (isVariableDeclarator() || hasMultipleVariableDeclarators()) { | |
Enumeration siblings= new SiblingEnumeration(getFirstFieldDeclaration()); | |
DOMField field= (DOMField)siblings.nextElement(); | |
DOMNode next= field.fNextNode; | |
while (siblings.hasMoreElements() && (next instanceof DOMField) && (((DOMField)next).isVariableDeclarator())) { | |
field.localizeContents(); | |
if (field.fParent != null) { | |
field.fParent.fragment(); | |
} | |
field= (DOMField)siblings.nextElement(); | |
next= field.fNextNode; | |
} | |
field.localizeContents(); | |
} | |
} | |
/** | |
* @see DOMNode#getDetailedNode() | |
*/ | |
protected DOMNode getDetailedNode() { | |
if (isVariableDeclarator() || hasMultipleVariableDeclarators()) { | |
return (DOMNode)getFactory().createField(new String(getSingleVariableDeclaratorContents())); | |
} else { | |
return (DOMNode)getFactory().createField(getContents()); | |
} | |
} | |
/** | |
* Returns the first field document fragment that defines | |
* the type for this variable declarator. | |
*/ | |
protected DOMField getFirstFieldDeclaration() { | |
if (isVariableDeclarator()) { | |
return ((DOMField)fPreviousNode).getFirstFieldDeclaration(); | |
} else { | |
return this; | |
} | |
} | |
/** | |
* @see IDOMField#getInitializer() | |
*/ | |
public String getInitializer() { | |
becomeDetailed(); | |
if (hasInitializer()) { | |
if (fInitializer != null) { | |
return fInitializer; | |
} else { | |
return CharArrayOps.substring(fDocument, fInitializerRange[0], fInitializerRange[1] + 1 - fInitializerRange[0]); | |
} | |
} else { | |
return null; | |
} | |
} | |
/** | |
* @see IDOMNode#getJavaElement | |
*/ | |
public IJavaElement getJavaElement(IJavaElement parent) throws IllegalArgumentException { | |
if (parent.getElementType() == IJavaElement.TYPE) { | |
return ((IType)parent).getField(getName()); | |
} else { | |
throw new IllegalArgumentException(Util.bind("element.illegalParent"/*nonNLS*/)); | |
} | |
} | |
/** | |
* Returns the last field document fragment in this muli-declarator statement. | |
*/ | |
protected DOMField getLastFieldDeclaration() { | |
DOMField field = this; | |
while (field.isVariableDeclarator() || field.hasMultipleVariableDeclarators()) { | |
if (field.fNextNode instanceof DOMField && ((DOMField)field.fNextNode).isVariableDeclarator()) { | |
field= (DOMField)field.fNextNode; | |
} else { | |
break; | |
} | |
} | |
return field; | |
} | |
/** | |
* @see DOMMember#getMemberDeclarationStartPosition() | |
*/ | |
protected int getMemberDeclarationStartPosition() { | |
return fTypeRange[0]; | |
} | |
/** | |
* @see IDOMNode#getNodeType() | |
*/ | |
public int getNodeType() { | |
return IDOMNode.FIELD; | |
} | |
/** | |
* Returns a String representing this field declaration as a field | |
* declaration with one variable declarator. | |
*/ | |
protected char[] getSingleVariableDeclaratorContents() { | |
CharArrayBuffer buffer= new CharArrayBuffer(); | |
DOMField first= getFirstFieldDeclaration(); | |
if (first.isDetailed()) { | |
first.appendMemberHeaderFragment(buffer); | |
buffer.append(getType()); | |
if (isVariableDeclarator()) { | |
buffer.append(' '); | |
} else { | |
buffer.append(fDocument, fTypeRange[1] + 1, fNameRange[0] - fTypeRange[1] - 1); | |
} | |
} else { | |
buffer.append(first.fDocument, first.fSourceRange[0], first.fNameRange[0] - first.fSourceRange[0]); | |
} | |
buffer.append(getName()); | |
if (hasInitializer()) { | |
if (fInitializerRange[0] < 0) { | |
buffer | |
.append('=') | |
.append(fInitializer) | |
.append(';') | |
.append(JavaModelManager.LINE_SEPARATOR); | |
} else { | |
buffer | |
.append(fDocument, fNameRange[1] + 1, fInitializerRange[0] - fNameRange[1] - 1) | |
.append(getInitializer()) | |
.append(';') | |
.append(JavaModelManager.LINE_SEPARATOR); | |
} | |
} else { | |
buffer.append(';').append(JavaModelManager.LINE_SEPARATOR); | |
} | |
return buffer.getContents(); | |
} | |
/** | |
* @see IDOMField#getType() | |
*/ | |
public String getType() { | |
return fType; | |
} | |
/** | |
* Returns the souce code to be used for this | |
* field's type. | |
*/ | |
protected char[] getTypeContents() { | |
if (isTypeAltered()) { | |
return fType.toCharArray(); | |
} else { | |
return CharArrayOps.subarray(fDocument, fTypeRange[0], fTypeRange[1] + 1 - fTypeRange[0]); | |
} | |
} | |
/** | |
* Returns true if this field has an initializer expression, | |
* otherwise false. | |
*/ | |
protected boolean hasInitializer() { | |
return getMask(MASK_FIELD_HAS_INITIALIZER); | |
} | |
/** | |
* Returns true is this field declarations has more than one | |
* variable declarator, otherwise false; | |
*/ | |
protected boolean hasMultipleVariableDeclarators() { | |
return fNextNode != null && (fNextNode instanceof DOMField) && | |
((DOMField)fNextNode).isVariableDeclarator(); | |
} | |
/** | |
* Inserts the given un-parented node as a sibling of this node, immediately before | |
* this node. Once inserted, the sibling is only dependent on this document fragment. | |
* | |
* <p>When a sibling is inserted before a variable declarator, it must first | |
* be expanded. | |
* | |
* @see IDOMNode#insertSibling(IDOMNode) | |
*/ | |
public void insertSibling(IDOMNode sibling) throws IllegalArgumentException, DOMException { | |
if (isVariableDeclarator()) { | |
expand(); | |
} | |
super.insertSibling(sibling); | |
} | |
/** | |
* Returns true if this field's type has been altered | |
* from the original document contents. | |
*/ | |
protected boolean isTypeAltered() { | |
return getMask(MASK_FIELD_TYPE_ALTERED); | |
} | |
/** | |
* Returns true if this field is declared as a secondary variable | |
* declarator for a previous field declaration. | |
*/ | |
protected boolean isVariableDeclarator() { | |
return getMask(MASK_FIELD_IS_VARIABLE_DECLARATOR); | |
} | |
/** | |
* @see DOMNode | |
*/ | |
protected DOMNode newDOMNode() { | |
return new DOMField(); | |
} | |
/** | |
* Normalizes this <code>DOMNode</code>'s end position. | |
*/ | |
void normalizeEndPosition(ILineStartFinder finder, DOMNode next) { | |
if (next == null) { | |
// this node's end position includes all of the characters up | |
// to the end of the enclosing node | |
DOMNode parent = (DOMNode) getParent(); | |
if (parent == null || parent instanceof DOMCompilationUnit) { | |
setSourceRangeEnd(fDocument.length - 1); | |
} else { | |
// parent is a type | |
setSourceRangeEnd(((DOMType)parent).getCloseBodyPosition() - 1); | |
} | |
} else { | |
// this node's end position is just before the start of the next node | |
// unless the next node is a field that is declared along with this one | |
next.normalizeStartPosition(getEndPosition(), finder); | |
if (next instanceof DOMField) { | |
DOMField field = (DOMField) next; | |
if (field.isVariableDeclarator() && fTypeRange[0] == field.fTypeRange[0]) | |
return; | |
} | |
setSourceRangeEnd(next.getStartPosition() - 1); | |
} | |
} | |
/** | |
* Normalizes this <code>DOMNode</code>'s start position. | |
*/ | |
void normalizeStartPosition(int endPosition, ILineStartFinder finder) { | |
if (isVariableDeclarator()) { | |
// start position is end of last element | |
setStartPosition(fPreviousNode.getEndPosition() + 1); | |
} else { | |
super.normalizeStartPosition(endPosition, finder); | |
} | |
} | |
/** | |
* Offsets all the source indexes in this node by the given amount. | |
*/ | |
protected void offset(int offset) { | |
super.offset(offset); | |
offsetRange(fInitializerRange, offset); | |
offsetRange(fTypeRange, offset); | |
} | |
/** | |
* Separates this node from its parent and siblings, maintaining any ties that | |
* this node has to the underlying document fragment. | |
* | |
* <p>When a field with multiple declarators is removed, its declaration | |
* must first be expanded. | |
* | |
* @see IDOMNode#remove() | |
*/ | |
public void remove() { | |
expand(); | |
super.remove(); | |
} | |
/** | |
* @see IDOMMember#setComment(String) | |
*/ | |
public void setComment(String comment) { | |
expand(); | |
super.setComment(comment); | |
} | |
/** | |
* @see IDOMMember#setFlags(int) | |
*/ | |
public void setFlags(int flags) { | |
expand(); | |
super.setFlags(flags); | |
} | |
/** | |
* Sets the state of this field declaration as having | |
* an initializer expression. | |
*/ | |
protected void setHasInitializer(boolean hasInitializer) { | |
setMask(MASK_FIELD_HAS_INITIALIZER, hasInitializer); | |
} | |
/** | |
* @see IDOMField#setInitializer(char[]) | |
*/ | |
public void setInitializer(String initializer) { | |
becomeDetailed(); | |
fragment(); | |
setHasInitializer(initializer != null); | |
fInitializer= initializer; | |
} | |
/** | |
* Sets the initializer range. | |
*/ | |
void setInitializerRange(int start, int end) { | |
fInitializerRange[0] = start; | |
fInitializerRange[1] = end; | |
} | |
/** | |
* Sets the state of this field declaration as being a | |
* secondary variable declarator for a previous field | |
* declaration. | |
*/ | |
protected void setIsVariableDeclarator(boolean isVariableDeclarator) { | |
setMask(MASK_FIELD_IS_VARIABLE_DECLARATOR, isVariableDeclarator); | |
} | |
/** | |
* @see IDOMField#setName(char[]) | |
*/ | |
public void setName(String name) throws IllegalArgumentException { | |
if (name == null) { | |
throw new IllegalArgumentException(Util.bind("element.nullName"/*nonNLS*/)); | |
} else { | |
super.setName(name); | |
setTypeAltered(true); | |
} | |
} | |
/** | |
* @see IDOMField#setType(char[]) | |
*/ | |
public void setType(String typeName) throws IllegalArgumentException { | |
if (typeName == null) { | |
throw new IllegalArgumentException(Util.bind("element.nullType"/*nonNLS*/)); | |
} | |
becomeDetailed(); | |
expand(); | |
fragment(); | |
setTypeAltered(true); | |
setNameAltered(true); | |
fType= typeName; | |
} | |
/** | |
* Sets the state of this field declaration as having | |
* the field type altered from the original document. | |
*/ | |
protected void setTypeAltered(boolean typeAltered) { | |
setMask(MASK_FIELD_TYPE_ALTERED, typeAltered); | |
} | |
/** | |
* @see DOMNode#shareContents(DOMNode) | |
*/ | |
protected void shareContents(DOMNode node) { | |
super.shareContents(node); | |
DOMField field= (DOMField)node; | |
fInitializer= field.fInitializer; | |
fInitializerRange= rangeCopy(field.fInitializerRange); | |
fType= field.fType; | |
fTypeRange= rangeCopy(field.fTypeRange); | |
} | |
/** | |
* @see IDOMNode#toString() | |
*/ | |
public String toString() { | |
return "FIELD: "/*nonNLS*/ + getName(); | |
} | |
} |