/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package net.sourceforge.phpdt.internal.core.jdom;
import java.util.Enumeration;
import net.sourceforge.phpdt.core.ICompilationUnit;
import net.sourceforge.phpdt.core.IJavaElement;
import net.sourceforge.phpdt.core.IType;
import net.sourceforge.phpdt.core.compiler.ITerminalSymbols;
import net.sourceforge.phpdt.core.compiler.ITerminalSymbols.TokenName;
import net.sourceforge.phpdt.core.compiler.InvalidInputException;
import net.sourceforge.phpdt.core.jdom.IDOMMethod;
import net.sourceforge.phpdt.core.jdom.IDOMNode;
import net.sourceforge.phpdt.core.jdom.IDOMType;
import net.sourceforge.phpdt.internal.compiler.parser.Scanner;
import net.sourceforge.phpdt.internal.compiler.util.Util;
import net.sourceforge.phpdt.internal.core.util.CharArrayBuffer;
import net.sourceforge.phpdt.internal.core.util.CharArrayOps;
/**
* DOMType provides an implementation of IDOMType.
*
* @see IDOMType
* @see DOMNode
*/
/* package */class DOMType extends DOMMember implements IDOMType {
/**
* The 'class' or 'interface' keyword if altered from the documents
* contents, otherwise null
.
*/
protected String fTypeKeyword;
/**
* The original inclusive source range of the 'class' or 'interface' keyword
* in the document.
*/
protected int[] fTypeRange;
/**
* The superclass name for the class declaration if altered from the
* document's contents, otherwise null
. Also
* null
when this type represents an interface.
*/
protected String fSuperclass;
/**
* The original inclusive source range of the superclass name in the
* document, or -1's of no superclass was specified in the document.
*/
protected int[] fSuperclassRange;
/**
* The original inclusive souce range of the 'extends' keyword in the
* document, including surrounding whitespace, or -1's if the keyword was
* not present in the document.
*/
protected int[] fExtendsRange;
/**
* The original inclusive souce range of the 'implements' keyword in the
* document, including surrounding whitespace, or -1's if the keyword was
* not present in the document.
*/
protected int[] fImplementsRange;
/**
* The comma delimited list of interfaces this type implements or extends,
* if altered from the document's contents, otherwise null
.
* Also null
if this type does not implement or extend any
* interfaces.
*/
protected char[] fInterfaces;
/**
* The original inclusive source range of the list of interfaces this type
* implements or extends, not including any surrouding whitespace. If the
* document did not specify interfaces, this array contains -1's.
*/
protected int[] fInterfacesRange;
/**
* The original source range of the first character following the type name
* superclass name, or interface list, up to and including the first
* character before the first type member.
*/
protected int[] fOpenBodyRange;
/**
* The original source range of the first new line or non whitespace
* character preceding the close brace of the type's body, up to the and
* including the first character before the next node (if there are no
* following nodes, the range ends at the position of the last character in
* the document).
*/
protected int[] fCloseBodyRange;
/**
* A list of interfaces this type extends or implements. null
* when this type does not extend or implement any interfaces.
*/
protected String[] fSuperInterfaces = new String[0];
/**
* This position is the position of the end of the last line separator
* before the closing brace starting position of the receiver.
*/
// protected int fInsertionPosition;
/**
* Constructs an empty type node.
*/
DOMType() {
}
/**
* Creates a new detailed TYPE 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 node, or
* null
if this node does not have a name
* @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
* immediately follow the name or -1's if this node does not have
* a 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 net.sourceforge.phpdt.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 (that is, package
* default visibility), this array contains two -1's.
* @param typeRange -
* a two element array describing the location of the 'class' or
* 'interface' keyword in the type declaration - first and last
* character positions.
* @param superclassRange -
* a two element array describing the location of the superclass
* name in the type declaration - first and last character
* positions or two -1's if a superclass is not present in the
* document.
* @param extendsRange -
* a two element array describing the location of the 'extends'
* keyword in the type declaration, including any surrounding
* whitespace, or -1's if the 'extends' keyword is not present in
* the document.
* @param implementsList -
* an array of names of the interfaces this type implements or
* extends, or null
if this type does not
* implement or extend any interfaces.
* @param implementsRange -
* a two element array describing the location of the comment
* delimited list of interfaces this type implements or extends,
* not including any surrounding whitespace, or -1's if no
* interface list is present in the document.
* @param implementsKeywordRange -
* a two element array describing the location of the
* 'implements' keyword, including any surrounding whitespace, or
* -1's if no 'implements' keyword is present in the document.
* @param openBodyRange -
* a two element array describing the location of the open brace
* of the type's body and whitespace following the type
* declaration and preceeding the first member in the type.
* @param closeBodyRange -
* a two element array describing the source range of the first
* new line or non whitespace character preceeding the close
* brace of the type's body, up to the close brace
* @param isClass -
* true is the type is a class, false if it is an interface
*/
DOMType(char[] document, int[] sourceRange, String name, int[] nameRange,
int[] commentRange, int flags, int[] modifierRange,
int[] typeRange, int[] superclassRange, int[] extendsRange,
String[] implementsList, int[] implementsRange,
int[] implementsKeywordRange, int[] openBodyRange,
int[] closeBodyRange, boolean isClass) {
super(document, sourceRange, name, nameRange, commentRange, flags,
modifierRange);
fTypeRange = typeRange;
setMask(MASK_TYPE_IS_CLASS, isClass);
fExtendsRange = extendsRange;
fImplementsRange = implementsKeywordRange;
fSuperclassRange = superclassRange;
fInterfacesRange = implementsRange;
fCloseBodyRange = closeBodyRange;
setMask(MASK_TYPE_HAS_SUPERCLASS, superclassRange[0] > 0);
setMask(MASK_TYPE_HAS_INTERFACES, implementsList != null);
fSuperInterfaces = implementsList;
fOpenBodyRange = openBodyRange;
fCloseBodyRange = closeBodyRange;
setMask(MASK_DETAILED_SOURCE_INDEXES, true);
}
/**
* Creates a new simple TYPE 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 node, or
* null
if this node does not have a name
* @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
* immediately follow the name or -1's if this node does not have
* a name.
* @param flags -
* an integer representing the modifiers for this member. The
* integer can be analyzed with net.sourceforge.phpdt.core.Flags
* @param implementsList -
* an array of names of the interfaces this type implements or
* extends, or null
if this type does not
* implement or extend any interfaces.
* @param isClass -
* true is the type is a class, false if it is an interface
*/
DOMType(char[] document, int[] sourceRange, String name, int[] nameRange,
int flags, String[] implementsList, boolean isClass) {
this(document, sourceRange, name, nameRange, new int[] { -1, -1 },
flags, new int[] { -1, -1 }, new int[] { -1, -1 }, new int[] {
-1, -1 }, new int[] { -1, -1 }, implementsList,
new int[] { -1, -1 }, new int[] { -1, -1 },
new int[] { -1, -1 }, new int[] { sourceRange[1],
sourceRange[1] }, isClass);
setMask(MASK_DETAILED_SOURCE_INDEXES, false);
}
/**
* @see IDOMType#addSuperInterface(String)
*/
public void addSuperInterface(String name) throws IllegalArgumentException {
if (name == null) {
throw new IllegalArgumentException(Util
.bind("dom.addNullInterface")); //$NON-NLS-1$
}
if (fSuperInterfaces == null) {
fSuperInterfaces = new String[1];
fSuperInterfaces[0] = name;
} else {
fSuperInterfaces = appendString(fSuperInterfaces, name);
}
setSuperInterfaces(fSuperInterfaces);
}
/**
* @see DOMMember#appendMemberBodyContents(CharArrayBuffer)
*/
protected void appendMemberBodyContents(CharArrayBuffer buffer) {
buffer.append(fDocument, fOpenBodyRange[0], fOpenBodyRange[1] + 1
- fOpenBodyRange[0]);
appendContentsOfChildren(buffer);
buffer.append(fDocument, fCloseBodyRange[0], fCloseBodyRange[1] + 1
- fCloseBodyRange[0]);
buffer.append(fDocument, fCloseBodyRange[1] + 1, fSourceRange[1]
- fCloseBodyRange[1]);
}
/**
* @see DOMMember#appendMemberDeclarationContents(CharArrayBuffer )
*/
protected void appendMemberDeclarationContents(CharArrayBuffer buffer) {
if (fTypeKeyword != null) {
buffer.append(fTypeKeyword);
buffer.append(fDocument, fTypeRange[1], fNameRange[0]
- fTypeRange[1]);
} else {
buffer.append(fDocument, fTypeRange[0], fTypeRange[1] + 1
- fTypeRange[0]);
}
buffer.append(getName());
if (isClass()) {
boolean hasSuperclass = false, hasInterfaces = false;
if (getMask(MASK_TYPE_HAS_SUPERCLASS)) {
hasSuperclass = true;
if (fExtendsRange[0] < 0) {
buffer.append(" extends "); //$NON-NLS-1$
} else {
buffer.append(fDocument, fExtendsRange[0], fExtendsRange[1]
+ 1 - fExtendsRange[0]);
}
if (fSuperclass != null) {
buffer.append(fSuperclass);
} else {
buffer.append(fDocument, fSuperclassRange[0],
fSuperclassRange[1] + 1 - fSuperclassRange[0]);
}
}
if (getMask(MASK_TYPE_HAS_INTERFACES)) {
hasInterfaces = true;
if (fImplementsRange[0] < 0) {
buffer.append(" implements "); //$NON-NLS-1$
} else {
buffer.append(fDocument, fImplementsRange[0],
fImplementsRange[1] + 1 - fImplementsRange[0]);
}
if (fInterfaces != null) {
buffer.append(fInterfaces);
} else {
buffer.append(fDocument, fInterfacesRange[0],
fInterfacesRange[1] + 1 - fInterfacesRange[0]);
}
}
if (hasInterfaces) {
if (fImplementsRange[0] < 0) {
buffer.append(' ');
} else {
buffer.append(fDocument, fInterfacesRange[1] + 1,
fOpenBodyRange[0] - fInterfacesRange[1] - 1);
}
} else {
if (hasSuperclass) {
if (fSuperclassRange[0] < 0) {
buffer.append(' ');
} else {
buffer.append(fDocument, fSuperclassRange[1] + 1,
fOpenBodyRange[0] - fSuperclassRange[1] - 1);
}
} else {
buffer.append(fDocument, fNameRange[1] + 1,
fOpenBodyRange[0] - fNameRange[1] - 1);
}
}
} else {
if (getMask(MASK_TYPE_HAS_INTERFACES)) {
if (fExtendsRange[0] < 0) {
buffer.append(" extends "); //$NON-NLS-1$
} else {
buffer.append(fDocument, fExtendsRange[0], fExtendsRange[1]
+ 1 - fExtendsRange[0]);
}
if (fInterfaces != null) {
buffer.append(fInterfaces);
buffer.append(' ');
} else {
buffer.append(fDocument, fInterfacesRange[0],
fInterfacesRange[1] + 1 - fInterfacesRange[0]);
}
} else {
buffer.append(fDocument, fNameRange[1] + 1, fOpenBodyRange[0]
- fNameRange[1] - 1);
}
}
}
/**
* @see DOMNode#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 and before my first child
buffer.append(fDocument, fNameRange[1] + 1, fOpenBodyRange[1]
- fNameRange[1]);
// append my children
appendContentsOfChildren(buffer);
// append from my last child to my end
buffer.append(fDocument, fCloseBodyRange[0], fSourceRange[1]
- fCloseBodyRange[0] + 1);
}
/**
* @see IDOMNode#canHaveChildren()
*/
public boolean canHaveChildren() {
return true;
}
/**
* Returns the position of the closing brace for the body of this type. This
* value this method returns is only valid before the type has been
* normalized and is present only for normalization.
*/
int getCloseBodyPosition() {
return fCloseBodyRange[0];
}
/**
* @see DOMNode#getDetailedNode()
*/
// protected DOMNode getDetailedNode() {
// return (DOMNode)getFactory().createType(getContents());
// }
/**
* @see DOMNode#getInsertionPosition()
*/
public int getInsertionPosition() {
// this should return the position of the end of the last line separator
// before the closing brace of the type
// See PR 1GELSDQ: ITPJUI:WINNT - JDOM: IType.createMethod does not
// insert nicely for inner types
return fInsertionPosition;
}
/**
* @see IDOMNode#getJavaElement
*/
public IJavaElement getJavaElement(IJavaElement parent)
throws IllegalArgumentException {
if (parent.getElementType() == IJavaElement.TYPE) {
return ((IType) parent).getType(getName());
} else if (parent.getElementType() == IJavaElement.COMPILATION_UNIT) {
return ((ICompilationUnit) parent).getType(getName());
} else {
throw new IllegalArgumentException(Util
.bind("element.illegalParent")); //$NON-NLS-1$
}
}
/**
* @see DOMMember#getMemberDeclarationStartPosition()
*/
protected int getMemberDeclarationStartPosition() {
return fTypeRange[0];
}
/**
* @see IDOMNode#getNodeType()
*/
public int getNodeType() {
return IDOMNode.TYPE;
}
/**
* Answers the open body range end position.
*/
int getOpenBodyEnd() {
return fOpenBodyRange[1];
}
/**
* @see IDOMType#getSuperclass()
*/
public String getSuperclass() {
becomeDetailed();
if (getMask(MASK_TYPE_HAS_SUPERCLASS)) {
if (fSuperclass != null) {
return fSuperclass;
} else {
return CharArrayOps.substring(fDocument, fSuperclassRange[0],
fSuperclassRange[1] + 1 - fSuperclassRange[0]);
}
} else {
return null;
}
}
/**
* @see IDOMType#getSuperInterfaces()
*/
public String[] getSuperInterfaces() {
return fSuperInterfaces;
}
/**
* @see IDOMNode
*/
public boolean isAllowableChild(IDOMNode node) {
if (node != null) {
int type = node.getNodeType();
return type == IDOMNode.TYPE || type == IDOMNode.FIELD
|| type == IDOMNode.METHOD || type == IDOMNode.INITIALIZER;
} else {
return false;
}
}
/**
* @see IDOMType#isClass()
*/
public boolean isClass() {
return getMask(MASK_TYPE_IS_CLASS);
}
/**
* @see DOMNode
*/
protected DOMNode newDOMNode() {
return new DOMType();
}
/**
* Normalizes this DOMNode
's source positions to include
* whitespace preceeding the node on the line on which the node starts, and
* all whitespace after the node up to the next node's start
*/
void normalize(ILineStartFinder finder) {
// perform final changes to the open and close body ranges
int openBodyEnd, openBodyStart, closeBodyStart, closeBodyEnd;
DOMNode first = (DOMNode) getFirstChild();
DOMNode lastNode = null;
// look for the open body
Scanner scanner = new Scanner();
scanner.setSource(fDocument);
scanner.resetTo(fNameRange[1] + 1, fDocument.length);
try {
TokenName currentToken = scanner.getNextToken();
while (currentToken != ITerminalSymbols.TokenName.LBRACE
&& currentToken != ITerminalSymbols.TokenName.EOF) {
currentToken = scanner.getNextToken();
}
if (currentToken == ITerminalSymbols.TokenName.LBRACE) {
openBodyEnd = scanner.currentPosition - 1;
openBodyStart = scanner.startPosition;
} else {
openBodyEnd = fDocument.length;
openBodyStart = fDocument.length;
}
} catch (InvalidInputException e) {
openBodyEnd = fDocument.length;
openBodyStart = fDocument.length;
}
if (first != null) {
int lineStart = finder.getLineStart(first.getStartPosition());
if (lineStart > openBodyEnd) {
openBodyEnd = lineStart - 1;
} else {
openBodyEnd = first.getStartPosition() - 1;
}
lastNode = (DOMNode) first.getNextNode();
if (lastNode == null) {
lastNode = first;
} else {
while (lastNode.getNextNode() != null) {
lastNode = (DOMNode) lastNode.getNextNode();
}
}
scanner.setSource(fDocument);
scanner.resetTo(lastNode.getEndPosition() + 1, fDocument.length);
try {
TokenName currentToken = scanner.getNextToken();
while (currentToken != ITerminalSymbols.TokenName.RBRACE
&& currentToken != ITerminalSymbols.TokenName.EOF) {
currentToken = scanner.getNextToken();
}
if (currentToken == ITerminalSymbols.TokenName.RBRACE) {
closeBodyStart = scanner.startPosition;
closeBodyEnd = scanner.currentPosition - 1;
} else {
closeBodyStart = fDocument.length;
closeBodyEnd = fDocument.length;
}
} catch (InvalidInputException e) {
closeBodyStart = fDocument.length;
closeBodyEnd = fDocument.length;
}
} else {
scanner.resetTo(openBodyEnd, fDocument.length);
try {
TokenName currentToken = scanner.getNextToken();
while (currentToken != ITerminalSymbols.TokenName.RBRACE
&& currentToken != ITerminalSymbols.TokenName.EOF) {
currentToken = scanner.getNextToken();
}
if (currentToken == ITerminalSymbols.TokenName.RBRACE) {
closeBodyStart = scanner.startPosition;
closeBodyEnd = scanner.currentPosition - 1;
} else {
closeBodyStart = fDocument.length;
closeBodyEnd = fDocument.length;
}
} catch (InvalidInputException e) {
closeBodyStart = fDocument.length;
closeBodyEnd = fDocument.length;
}
openBodyEnd = closeBodyEnd - 1;
}
setOpenBodyRangeEnd(openBodyEnd);
setOpenBodyRangeStart(openBodyStart);
setCloseBodyRangeStart(closeBodyStart);
setCloseBodyRangeEnd(closeBodyEnd);
fInsertionPosition = finder.getLineStart(closeBodyStart);
if (lastNode != null && fInsertionPosition < lastNode.getEndPosition()) {
fInsertionPosition = getCloseBodyPosition();
}
if (fInsertionPosition <= openBodyEnd) {
fInsertionPosition = getCloseBodyPosition();
}
super.normalize(finder);
}
/**
* Normalizes this DOMNode
'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
next.normalizeStartPosition(getEndPosition(), finder);
setSourceRangeEnd(next.getStartPosition() - 1);
}
}
/**
* Offsets all the source indexes in this node by the given amount.
*/
protected void offset(int offset) {
super.offset(offset);
offsetRange(fCloseBodyRange, offset);
offsetRange(fExtendsRange, offset);
offsetRange(fImplementsRange, offset);
offsetRange(fInterfacesRange, offset);
offsetRange(fOpenBodyRange, offset);
offsetRange(fSuperclassRange, offset);
offsetRange(fTypeRange, offset);
}
/**
* @see IDOMType#setClass(boolean)
*/
public void setClass(boolean b) {
becomeDetailed();
fragment();
setMask(MASK_TYPE_IS_CLASS, b);
if (b) {
fTypeKeyword = "class"; //$NON-NLS-1$
} else {
fTypeKeyword = "interface"; //$NON-NLS-1$
setSuperclass(null);
}
}
/**
* Sets the end of the close body range
*/
void setCloseBodyRangeEnd(int end) {
fCloseBodyRange[1] = end;
}
/**
* Sets the start of the close body range
*/
void setCloseBodyRangeStart(int start) {
fCloseBodyRange[0] = start;
}
/**
* Sets the name of this node.
*
*
* When the name of a type is set, all of its constructors must be marked as * fragmented, since the names of the constructors must reflect the name of * this type. * * @see IDOMNode#setName(char[]) */ public void setName(String name) throws IllegalArgumentException { if (name == null) { throw new IllegalArgumentException(Util.bind("element.nullName")); //$NON-NLS-1$ } super.setName(name); Enumeration children = getChildren(); while (children.hasMoreElements()) { IDOMNode child = (IDOMNode) children.nextElement(); if (child.getNodeType() == IDOMNode.METHOD && ((IDOMMethod) child).isConstructor()) { ((DOMNode) child).fragment(); } } } /** * Sets the end of the open body range */ void setOpenBodyRangeEnd(int end) { fOpenBodyRange[1] = end; } /** * Sets the start of the open body range */ void setOpenBodyRangeStart(int start) { fOpenBodyRange[0] = start; } /** * @see IDOMType#setSuperclass(char[]) */ public void setSuperclass(String superclassName) { becomeDetailed(); fragment(); fSuperclass = superclassName; setMask(MASK_TYPE_HAS_SUPERCLASS, superclassName != null); } /** * @see IDOMType#setSuperInterfaces(String[]) */ public void setSuperInterfaces(String[] names) { becomeDetailed(); if (names == null) { throw new IllegalArgumentException(Util.bind("dom.nullInterfaces")); //$NON-NLS-1$ } fragment(); fSuperInterfaces = names; if (names == null || names.length == 0) { fInterfaces = null; fSuperInterfaces = null; setMask(MASK_TYPE_HAS_INTERFACES, false); } else { setMask(MASK_TYPE_HAS_INTERFACES, true); CharArrayBuffer buffer = new CharArrayBuffer(); for (int i = 0; i < names.length; i++) { if (i > 0) { buffer.append(", "); //$NON-NLS-1$ } buffer.append(names[i]); } fInterfaces = buffer.getContents(); } } /** * Sets the type keyword */ // void setTypeKeyword(String keyword) { // fTypeKeyword = keyword; // } /** * @see DOMNode#shareContents(DOMNode) */ protected void shareContents(DOMNode node) { super.shareContents(node); DOMType type = (DOMType) node; fCloseBodyRange = rangeCopy(type.fCloseBodyRange); fExtendsRange = type.fExtendsRange; fImplementsRange = rangeCopy(type.fImplementsRange); fInterfaces = type.fInterfaces; fInterfacesRange = rangeCopy(type.fInterfacesRange); fOpenBodyRange = rangeCopy(type.fOpenBodyRange); fSuperclass = type.fSuperclass; fSuperclassRange = rangeCopy(type.fSuperclassRange); fSuperInterfaces = type.fSuperInterfaces; fTypeKeyword = type.fTypeKeyword; fTypeRange = rangeCopy(type.fTypeRange); } /** * @see IDOMNode#toString() */ public String toString() { return "TYPE: " + getName(); //$NON-NLS-1$ } }