/******************************************************************************* * 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$ } }