/******************************************************************************* * 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 net.sourceforge.phpdt.core.IJavaElement; import net.sourceforge.phpdt.core.IType; import net.sourceforge.phpdt.core.Signature; import net.sourceforge.phpdt.core.jdom.IDOMMethod; import net.sourceforge.phpdt.core.jdom.IDOMNode; import net.sourceforge.phpdt.internal.compiler.util.Util; import net.sourceforge.phpdt.internal.core.util.CharArrayBuffer; import net.sourceforge.phpdt.internal.core.util.CharArrayOps; /** * DOMMethod provides an implementation of IDOMMethod. * * @see IDOMMethod * @see DOMNode */ class DOMMethod extends DOMMember implements IDOMMethod { /** * Contains the return type of the method when the return type has been * altered from the contents in the document, otherwise null. */ protected String fReturnType; /** * The original inclusive source range of the method's return type in the * document, or -1's if no return type is present in the document. If the * return type of this method is qualified with '[]' following the parameter * list, this array has four entries. In this case, the last two entries of * the array are the inclusive source range of the array qualifiers. */ protected int[] fReturnTypeRange; /** * Contains the textual representation of the method's parameter list, * including open and closing parentheses when the parameters had been * altered from the contents in the document, otherwise null. */ protected char[] fParameterList; /** * The original inclusive source range of the method's parameter list in the * document. */ protected int[] fParameterRange; /** * Contains the textual representation of the method's exception list when * the exceptions had been altered from the contents in the document, * otherwise null. The exception list is a comment delimited * list of exceptions, not including the "throws" keyword. */ protected char[] fExceptionList; /** * The original inclusive source range of the method's exception list in the * document. */ protected int[] fExceptionRange; /** * Contains the method's body when the body has been altered from the * contents in the document, otherwise null. The body * includes everything between and including the enclosing braces, and * trailing whitespace. */ protected String fBody; /** * The original inclusive source range of the method's body. */ protected int[] fBodyRange; /** * Names of parameters in the method parameter list, or null * if the method has no parameters. */ protected String[] fParameterNames; /** * Types of parameters in the method parameter list, or null * if the method has no parameters. */ protected String[] fParameterTypes; /** * The exceptions the method throws, or null if the method * throws no exceptions. */ protected String[] fExceptions; /** * Constructs an empty method node. */ DOMMethod() { } /** * Creates a new detailed METHOD 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 isConstructor - * true if the method is a contructor, otherwise false * @param returnType - * the normalized return type of this method * @param returnTypeRange - * a two element array describing the location of the return type * within the method's source range. The first integer is is the * position of the first character in the return type, and the * second integer is the position of the last character in the * return type. For constructors, the contents of this array are * -1's. If the return type of this method is qualified with '[]' * following the parameter list, this array has four entries. In * this case, the last two entries of the array are the inclusive * source range of the array qualifiers. * @param parameterTypes - * an array of parameter types in the method declaration or * null if the method has no parameters * @param parameterNames - * an array of parameter names in the method declaration or * null if the method has no parameters * @param parameterRange - * a two element array describing the location of the parameter * list in the method. The first integer is the location of the * open parenthesis and the second integer is the location of the * closing parenthesis. * @param exceptions - * an array of the names of exceptions thrown by this method or * null if the method throws no exceptions * @param exceptionRange - * a two element array describing the location of the exception * list in the method declaration. The first integer is the * position of the first character in the first exception the * method throws, and the second integer is the position of the * last character of the last exception this method throws. * @param bodyRange - * a two element array describing the location of the method's * body. The first integer is the first character following the * method's parameter list, or exception list (if present). The * second integer is the location of the last character in the * method's source range. */ DOMMethod(char[] document, int[] sourceRange, String name, int[] nameRange, int[] commentRange, int flags, int[] modifierRange, boolean isConstructor, String returnType, int[] returnTypeRange, String[] parameterTypes, String[] parameterNames, int[] parameterRange, String[] exceptions, int[] exceptionRange, int[] bodyRange) { super(document, sourceRange, name, nameRange, commentRange, flags, modifierRange); setMask(MASK_IS_CONSTRUCTOR, isConstructor); fReturnType = returnType; fReturnTypeRange = returnTypeRange; fParameterTypes = parameterTypes; fParameterNames = parameterNames; fParameterRange = parameterRange; fExceptionRange = exceptionRange; fExceptions = exceptions; setHasBody(true); fBodyRange = bodyRange; setMask(MASK_DETAILED_SOURCE_INDEXES, true); } /** * Creates a new simple METHOD 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 isConstructor - * true if the method is a contructor, otherwise false * @param returnType - * the normalized return type of this method * @param parameterTypes - * an array of parameter types in the method declaration or * null if the method has no parameters * @param parameterNames - * an array of parameter names in the method declaration or * null if the method has no parameters * @param exceptions - * an array of the names of exceptions thrown by this method or * null if the method throws no exceptions */ DOMMethod(char[] document, int[] sourceRange, String name, int[] nameRange, int flags, boolean isConstructor, String returnType, String[] parameterTypes, String[] parameterNames, String[] exceptions) { this(document, sourceRange, name, nameRange, new int[] { -1, -1 }, flags, new int[] { -1, -1 }, isConstructor, returnType, new int[] { -1, -1 }, parameterTypes, parameterNames, new int[] { -1, -1 }, exceptions, new int[] { -1, -1 }, new int[] { -1, -1 }); setMask(MASK_DETAILED_SOURCE_INDEXES, false); } /** * @see IDOMMethod#addException(String) */ public void addException(String name) throws IllegalArgumentException { if (name == null) { throw new IllegalArgumentException(Util .bind("dom.nullExceptionType")); //$NON-NLS-1$ } if (fExceptions == null) { fExceptions = new String[1]; fExceptions[0] = name; } else { fExceptions = appendString(fExceptions, name); } setExceptions(fExceptions); } /** * @see IDOMMethod#addParameter(String, String) */ public void addParameter(String type, String name) throws IllegalArgumentException { if (type == null) { throw new IllegalArgumentException(Util .bind("dom.nullTypeParameter")); //$NON-NLS-1$ } if (name == null) { throw new IllegalArgumentException(Util .bind("dom.nullNameParameter")); //$NON-NLS-1$ } if (fParameterNames == null) { fParameterNames = new String[1]; fParameterNames[0] = name; } else { fParameterNames = appendString(fParameterNames, name); } if (fParameterTypes == null) { fParameterTypes = new String[1]; fParameterTypes[0] = type; } else { fParameterTypes = appendString(fParameterTypes, type); } setParameters(fParameterTypes, fParameterNames); } /** * @see DOMMember#appendMemberBodyContents(CharArrayBuffer) */ protected void appendMemberBodyContents(CharArrayBuffer buffer) { if (fBody != null) { buffer.append(fBody); } else { buffer.append(fDocument, fBodyRange[0], fBodyRange[1] + 1 - fBodyRange[0]); } } /** * @see DOMMember#appendMemberDeclarationContents(CharArrayBuffer) */ protected void appendMemberDeclarationContents(CharArrayBuffer buffer) { if (isConstructor()) { buffer.append(getConstructorName()).append(fDocument, fNameRange[1] + 1, fParameterRange[0] - fNameRange[1] - 1); } else { buffer.append(getReturnTypeContents()); if (fReturnTypeRange[0] >= 0) { buffer.append(fDocument, fReturnTypeRange[1] + 1, fNameRange[0] - fReturnTypeRange[1] - 1); } else { buffer.append(' '); } buffer.append(getNameContents()).append(fDocument, fNameRange[1] + 1, fParameterRange[0] - fNameRange[1] - 1); } if (fParameterList != null) { buffer.append(fParameterList); } else { buffer.append(fDocument, fParameterRange[0], fParameterRange[1] + 1 - fParameterRange[0]); } int start; if (hasTrailingArrayQualifier() && isReturnTypeAltered()) { start = fReturnTypeRange[3] + 1; } else { start = fParameterRange[1] + 1; } if (fExceptions != null) { // add 'throws' keyword if (fExceptionRange[0] >= 0) { buffer.append(fDocument, start, fExceptionRange[0] - start); } else { buffer.append(" throws "); //$NON-NLS-1$ } // add exception list if (fExceptionList != null) { buffer.append(fExceptionList); // add space before body if (fExceptionRange[0] >= 0) { buffer.append(fDocument, fExceptionRange[1] + 1, fBodyRange[0] - fExceptionRange[1] - 1); } else { buffer.append(fDocument, fParameterRange[1] + 1, fBodyRange[0] - fParameterRange[1] - 1); } } else { // add list and space before body buffer.append(fDocument, fExceptionRange[0], fBodyRange[0] - fExceptionRange[0]); } } else { // add space before body if (fExceptionRange[0] >= 0) { buffer.append(fDocument, fExceptionRange[1] + 1, fBodyRange[0] - fExceptionRange[1] - 1); } else { buffer.append(fDocument, start, fBodyRange[0] - start); } } } /** * @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 if (isConstructor()) { buffer.append(getConstructorName()); } else { buffer.append(fName); } // append everything after my name buffer.append(fDocument, fNameRange[1] + 1, fSourceRange[1] - fNameRange[1]); } /** * @see IDOMMethod#getBody() */ public String getBody() { becomeDetailed(); if (hasBody()) { if (fBody != null) { return fBody; } else { return CharArrayOps.substring(fDocument, fBodyRange[0], fBodyRange[1] + 1 - fBodyRange[0]); } } else { return null; } } /** * Returns the simple name of the enclsoing type for this constructor. If * the constuctor is not currently enclosed in a type, the original name of * the constructor as found in the documnent is returned. */ protected String getConstructorName() { if (isConstructor()) { if (getParent() != null) { return getParent().getName(); } else { // If there is no parent use the original name return new String(getNameContents()); } } else { return null; } } /** * @see DOMNode#getDetailedNode() */ // protected DOMNode getDetailedNode() { // return (DOMNode)getFactory().createMethod(getContents()); // } /** * @see IDOMMethod#getExceptions() */ public String[] getExceptions() { return fExceptions; } /** * @see IDOMNode#getJavaElement */ public IJavaElement getJavaElement(IJavaElement parent) throws IllegalArgumentException { if (parent.getElementType() == IJavaElement.TYPE) { // translate parameter types to signatures String[] sigs = null; if (fParameterTypes != null) { sigs = new String[fParameterTypes.length]; int i; for (i = 0; i < fParameterTypes.length; i++) { sigs[i] = Signature.createTypeSignature(fParameterTypes[i] .toCharArray(), false); } } String name = null; if (isConstructor()) { name = getConstructorName(); } else { name = getName(); } return ((IType) parent).getMethod(name, sigs); } else { throw new IllegalArgumentException(Util .bind("element.illegalParent")); //$NON-NLS-1$ } } /** * @see DOMMember#getMemberDeclarationStartPosition() */ protected int getMemberDeclarationStartPosition() { if (fReturnTypeRange[0] >= 0) { return fReturnTypeRange[0]; } else { return fNameRange[0]; } } /** * @see IDOMNode#getName() */ public String getName() { if (isConstructor()) { return null; } else { return super.getName(); } } /** * @see IDOMNode#getNodeType() */ public int getNodeType() { return IDOMNode.METHOD; } /** * @see IDOMMethod#getParameterNames() */ public String[] getParameterNames() { return fParameterNames; } /** * @see IDOMMethod#getParameterTypes() */ public String[] getParameterTypes() { return fParameterTypes; } /** * @see IDOMMethod#getReturnType() */ public String getReturnType() { if (isConstructor()) { return null; } else { return fReturnType; } } /** * Returns the source code to be used for this method's return type */ protected char[] getReturnTypeContents() { if (isConstructor()) { return null; } else { if (isReturnTypeAltered()) { return fReturnType.toCharArray(); } else { return CharArrayOps.subarray(fDocument, fReturnTypeRange[0], fReturnTypeRange[1] + 1 - fReturnTypeRange[0]); } } } /** * Returns true if this method's return type has array qualifiers ('[]') * following the parameter list. */ protected boolean hasTrailingArrayQualifier() { return fReturnTypeRange.length > 2; } /** * @see IDOMMethod#isConstructor() */ public boolean isConstructor() { return getMask(MASK_IS_CONSTRUCTOR); } /** * Returns true if this method's return type has been altered from the * original document contents. */ protected boolean isReturnTypeAltered() { return getMask(MASK_RETURN_TYPE_ALTERED); } /** * @see IDOMNode#isSigantureEqual(IDOMNode). * *

* Two methods have equal signatures if there names are the same and their * parameter types are the same. */ public boolean isSignatureEqual(IDOMNode node) { boolean ok = node.getNodeType() == getNodeType(); if (ok) { IDOMMethod method = (IDOMMethod) node; ok = (isConstructor() && method.isConstructor()) || (!isConstructor() && !method.isConstructor()); if (ok && !isConstructor()) { ok = getName().equals(method.getName()); } if (!ok) { return false; } String[] types = method.getParameterTypes(); if (fParameterTypes == null || fParameterTypes.length == 0) { // this method has no parameters if (types == null || types.length == 0) { // the other method has no parameters either return true; } } else { // this method has parameters if (types == null || types.length == 0) { // the other method has no parameters return false; } if (fParameterTypes.length != types.length) { // the methods have a different number of parameters return false; } int i; for (i = 0; i < types.length; i++) { if (!fParameterTypes[i].equals(types[i])) { return false; } } return true; } } return false; } /** * @see DOMNode */ protected DOMNode newDOMNode() { return new DOMMethod(); } /** * Offsets all the source indexes in this node by the given amount. */ protected void offset(int offset) { super.offset(offset); offsetRange(fBodyRange, offset); offsetRange(fExceptionRange, offset); offsetRange(fParameterRange, offset); offsetRange(fReturnTypeRange, offset); } /** * @see IDOMMethod#setBody */ public void setBody(String body) { becomeDetailed(); fragment(); fBody = body; setHasBody(body != null); if (!hasBody()) { fBody = ";" + Util.LINE_SEPARATOR; //$NON-NLS-1$ } } /** * Sets the end of the body range */ void setBodyRangeEnd(int end) { fBodyRange[1] = end; } /** * @see IDOMMethod#setConstructor(boolean) */ public void setConstructor(boolean b) { becomeDetailed(); setMask(MASK_IS_CONSTRUCTOR, b); fragment(); } /** * @see IDOMMethod#setExceptions(char[][]) */ public void setExceptions(String[] names) { becomeDetailed(); if (names == null || names.length == 0) { fExceptions = null; } else { fExceptions = names; CharArrayBuffer buffer = new CharArrayBuffer(); char[] comma = new char[] { ',', ' ' }; for (int i = 0, length = names.length; i < length; i++) { if (i > 0) buffer.append(comma); buffer.append(names[i]); } fExceptionList = buffer.getContents(); } fragment(); } /** * @see IDOMMethod#setName */ public void setName(String name) { if (name == null) { throw new IllegalArgumentException(Util.bind("element.nullName")); //$NON-NLS-1$ } else { super.setName(name); } } /** * @see IDOMMethod#setParameters(char[][], char[][]) */ public void setParameters(String[] types, String[] names) throws IllegalArgumentException { becomeDetailed(); if (types == null || names == null) { if (types == null && names == null) { fParameterTypes = null; fParameterNames = null; fParameterList = new char[] { '(', ')' }; } else { throw new IllegalArgumentException(Util .bind("dom.mismatchArgNamesAndTypes")); //$NON-NLS-1$ } } else if (names.length != types.length) { throw new IllegalArgumentException(Util .bind("dom.mismatchArgNamesAndTypes")); //$NON-NLS-1$ } else if (names.length == 0) { setParameters(null, null); } else { fParameterNames = names; fParameterTypes = types; CharArrayBuffer parametersBuffer = new CharArrayBuffer(); parametersBuffer.append("("); //$NON-NLS-1$ char[] comma = new char[] { ',', ' ' }; for (int i = 0; i < names.length; i++) { if (i > 0) { parametersBuffer.append(comma); } parametersBuffer.append(types[i]).append(' ').append(names[i]); } parametersBuffer.append(')'); fParameterList = parametersBuffer.getContents(); } fragment(); } /** * @see IDOMMethod#setReturnType(char[]) */ public void setReturnType(String name) throws IllegalArgumentException { if (name == null) { throw new IllegalArgumentException(Util.bind("dom.nullReturnType")); //$NON-NLS-1$ } becomeDetailed(); fragment(); setReturnTypeAltered(true); fReturnType = name; } /** * Sets the state of this method declaration as having the return type * altered from the original document. */ protected void setReturnTypeAltered(boolean typeAltered) { setMask(MASK_RETURN_TYPE_ALTERED, typeAltered); } /** */ protected void setSourceRangeEnd(int end) { super.setSourceRangeEnd(end); fBodyRange[1] = end; } /** * @see DOMNode#shareContents(DOMNode) */ protected void shareContents(DOMNode node) { super.shareContents(node); DOMMethod method = (DOMMethod) node; fBody = method.fBody; fBodyRange = rangeCopy(method.fBodyRange); fExceptionList = method.fExceptionList; fExceptionRange = rangeCopy(method.fExceptionRange); fExceptions = method.fExceptions; fParameterList = method.fParameterList; fParameterNames = method.fParameterNames; fParameterRange = rangeCopy(method.fParameterRange); fParameterTypes = method.fParameterTypes; fReturnType = method.fReturnType; fReturnTypeRange = rangeCopy(method.fReturnTypeRange); } /** * @see IDOMNode#toString() */ public String toString() { if (isConstructor()) { return "CONSTRUCTOR"; //$NON-NLS-1$ } else { return "METHOD: " + getName(); //$NON-NLS-1$ } } }