/******************************************************************************* * Copyright (c) 2000, 2009 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.core; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.core.Flags; import org.eclipse.jdt.core.IAnnotation; import org.eclipse.jdt.core.IMemberValuePair; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.ITypeParameter; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.Signature; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation; import org.eclipse.jdt.internal.compiler.env.IBinaryMethod; import org.eclipse.jdt.internal.compiler.env.IBinaryType; import org.eclipse.jdt.internal.compiler.lookup.Binding; import org.eclipse.jdt.internal.core.JavaModelManager.PerProjectInfo; import org.eclipse.jdt.internal.core.util.Util; /** * @see IMethod */ /* package */class BinaryMethod extends BinaryMember implements IMethod { /** * The parameter type signatures of the method - stored locally to perform equality test. * <code>null</code> indicates no parameters. */ protected String[] parameterTypes; /** * The parameter names for the method. */ protected String[] parameterNames; protected String[] exceptionTypes; protected String returnType; protected BinaryMethod(JavaElement parent, String name, String[] paramTypes) { super(parent, name); // Assertion disabled since bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=179011 // Assert.isTrue(name.indexOf('.') == -1); if (paramTypes == null) { this.parameterTypes= CharOperation.NO_STRINGS; } else { this.parameterTypes= paramTypes; } } public boolean equals(Object o) { if (!(o instanceof BinaryMethod)) return false; return super.equals(o) && Util.equalArraysOrNull(this.parameterTypes, ((BinaryMethod)o).parameterTypes); } public IAnnotation[] getAnnotations() throws JavaModelException { IBinaryMethod info= (IBinaryMethod)getElementInfo(); IBinaryAnnotation[] binaryAnnotations= info.getAnnotations(); return getAnnotations(binaryAnnotations, info.getTagBits()); } public IMemberValuePair getDefaultValue() throws JavaModelException { IBinaryMethod info= (IBinaryMethod)getElementInfo(); Object defaultValue= info.getDefaultValue(); if (defaultValue == null) return null; MemberValuePair memberValuePair= new MemberValuePair(getElementName()); memberValuePair.value= Util.getAnnotationMemberValue(this, memberValuePair, defaultValue); return memberValuePair; } /* * @see IMethod */ public String[] getExceptionTypes() throws JavaModelException { if (this.exceptionTypes == null) { IBinaryMethod info= (IBinaryMethod)getElementInfo(); char[] genericSignature= info.getGenericSignature(); if (genericSignature != null) { char[] dotBasedSignature= CharOperation.replaceOnCopy(genericSignature, '/', '.'); this.exceptionTypes= Signature.getThrownExceptionTypes(new String(dotBasedSignature)); } if (this.exceptionTypes == null || this.exceptionTypes.length == 0) { char[][] eTypeNames= info.getExceptionTypeNames(); if (eTypeNames == null || eTypeNames.length == 0) { this.exceptionTypes= CharOperation.NO_STRINGS; } else { eTypeNames= ClassFile.translatedNames(eTypeNames); this.exceptionTypes= new String[eTypeNames.length]; for (int j= 0, length= eTypeNames.length; j < length; j++) { // 1G01HRY: ITPJCORE:WINNT - method.getExceptionType not in correct format int nameLength= eTypeNames[j].length; char[] convertedName= new char[nameLength + 2]; System.arraycopy(eTypeNames[j], 0, convertedName, 1, nameLength); convertedName[0]= 'L'; convertedName[nameLength + 1]= ';'; this.exceptionTypes[j]= new String(convertedName); } } } } return this.exceptionTypes; } /* * @see IJavaElement */ public int getElementType() { return METHOD; } /* * @see IMember */ public int getFlags() throws JavaModelException { IBinaryMethod info= (IBinaryMethod)getElementInfo(); return info.getModifiers(); } /* * @see JavaElement#getHandleMemento(StringBuffer) */ protected void getHandleMemento(StringBuffer buff) { ((JavaElement)getParent()).getHandleMemento(buff); char delimiter= getHandleMementoDelimiter(); buff.append(delimiter); escapeMementoName(buff, getElementName()); for (int i= 0; i < this.parameterTypes.length; i++) { buff.append(delimiter); escapeMementoName(buff, this.parameterTypes[i]); } if (this.occurrenceCount > 1) { buff.append(JEM_COUNT); buff.append(this.occurrenceCount); } } /* * @see JavaElement#getHandleMemento() */ protected char getHandleMementoDelimiter() { return JavaElement.JEM_METHOD; } public String getKey(boolean forceOpen) throws JavaModelException { return getKey(this, forceOpen); } /* * @see IMethod */ public int getNumberOfParameters() { return this.parameterTypes == null ? 0 : this.parameterTypes.length; } /* * @see IMethod * Look for source attachment information to retrieve the actual parameter names as stated in source. */ public String[] getParameterNames() throws JavaModelException { if (this.parameterNames != null) return this.parameterNames; // force source mapping if not already done IType type= (IType)getParent(); SourceMapper mapper= getSourceMapper(); if (mapper != null) { char[][] paramNames= mapper.getMethodParameterNames(this); // map source and try to find parameter names if (paramNames == null) { IBinaryType info= (IBinaryType)((BinaryType)getDeclaringType()).getElementInfo(); char[] source= mapper.findSource(type, info); if (source != null) { mapper.mapSource(type, source, info); } paramNames= mapper.getMethodParameterNames(this); } // if parameter names exist, convert parameter names to String array if (paramNames != null) { this.parameterNames= new String[paramNames.length]; for (int i= 0; i < paramNames.length; i++) { this.parameterNames[i]= new String(paramNames[i]); } return this.parameterNames; } } // try to see if we can retrieve the names from the attached javadoc IBinaryMethod info= (IBinaryMethod)getElementInfo(); final int paramCount= Signature.getParameterCount(new String(info.getMethodDescriptor())); if (paramCount != 0) { // don't try to look for javadoc for synthetic methods int modifiers= getFlags(); if ((modifiers & ClassFileConstants.AccSynthetic) != 0) { return this.parameterNames= getRawParameterNames(paramCount); } JavadocContents javadocContents= null; IType declaringType= getDeclaringType(); PerProjectInfo projectInfo= JavaModelManager.getJavaModelManager().getPerProjectInfoCheckExistence(getJavaProject().getProject()); synchronized (projectInfo.javadocCache) { javadocContents= (JavadocContents)projectInfo.javadocCache.get(declaringType); if (javadocContents == null) { projectInfo.javadocCache.put(declaringType, BinaryType.EMPTY_JAVADOC); } } String methodDoc= null; if (javadocContents == null) { long timeOut= 50; // default value try { String option= getJavaProject().getOption(JavaCore.TIMEOUT_FOR_PARAMETER_NAME_FROM_ATTACHED_JAVADOC, true); if (option != null) { timeOut= Long.parseLong(option); } } catch (NumberFormatException e) { // ignore } if (timeOut == 0) { // don't try to fetch the values return this.parameterNames= getRawParameterNames(paramCount); } final class ParametersNameCollector { String javadoc; public void setJavadoc(String s) { this.javadoc= s; } public String getJavadoc() { return this.javadoc; } } /* * The declaring type is not in the cache yet. The thread wil retrieve the javadoc contents */ final ParametersNameCollector nameCollector= new ParametersNameCollector(); Thread collect= new Thread() { public void run() { try { // this call has a side-effect on the per project info cache nameCollector.setJavadoc(BinaryMethod.this.getAttachedJavadoc(null)); } catch (JavaModelException e) { // ignore } synchronized (nameCollector) { nameCollector.notify(); } } }; collect.start(); synchronized (nameCollector) { try { nameCollector.wait(timeOut); } catch (InterruptedException e) { // ignore } } methodDoc= nameCollector.getJavadoc(); } else if (javadocContents != BinaryType.EMPTY_JAVADOC) { // need to extract the part relative to the binary method since javadoc contains the javadoc for the declaring type try { methodDoc= javadocContents.getMethodDoc(this); } catch (JavaModelException e) { javadocContents= null; } } if (methodDoc != null) { final int indexOfOpenParen= methodDoc.indexOf('('); if (indexOfOpenParen != -1) { final int indexOfClosingParen= methodDoc.indexOf(')', indexOfOpenParen); if (indexOfClosingParen != -1) { final char[] paramsSource= CharOperation.replace( methodDoc.substring(indexOfOpenParen + 1, indexOfClosingParen).toCharArray(), " ".toCharArray(), //$NON-NLS-1$ new char[] { ' ' }); final char[][] params= splitParameters(paramsSource, paramCount); final int paramsLength= params.length; this.parameterNames= new String[paramsLength]; for (int i= 0; i < paramsLength; i++) { final char[] param= params[i]; int indexOfSpace= CharOperation.lastIndexOf(' ', param); if (indexOfSpace != -1) { this.parameterNames[i]= String.valueOf(param, indexOfSpace + 1, param.length - indexOfSpace - 1); } else { this.parameterNames[i]= "arg" + i; //$NON-NLS-1$ } } return this.parameterNames; } } } // let's see if we can retrieve them from the debug infos char[][] argumentNames= info.getArgumentNames(); if (argumentNames != null && argumentNames.length == paramCount) { String[] names= new String[paramCount]; for (int i= 0; i < paramCount; i++) { names[i]= new String(argumentNames[i]); } return this.parameterNames= names; } } // if still no parameter names, produce fake ones return this.parameterNames= getRawParameterNames(paramCount); } private char[][] splitParameters(char[] parametersSource, int paramCount) { // we have generic types as one of the parameter types char[][] params= new char[paramCount][]; int paramIndex= 0; int index= 0; int balance= 0; int length= parametersSource.length; int start= 0; while (index < length) { switch (parametersSource[index]) { case '<': balance++; index++; while (index < length && parametersSource[index] != '>') { index++; } break; case '>': balance--; index++; break; case ',': if (balance == 0 && paramIndex < paramCount) { params[paramIndex++]= CharOperation.subarray(parametersSource, start, index); start= index + 1; } index++; break; case '&': if ((index + 4) < length) { if (parametersSource[index + 1] == 'l' && parametersSource[index + 2] == 't' && parametersSource[index + 3] == ';') { balance++; index+= 4; } else if (parametersSource[index + 1] == 'g' && parametersSource[index + 2] == 't' && parametersSource[index + 3] == ';') { balance--; index+= 4; } else { index++; } } else { index++; } break; default: index++; } } if (paramIndex < paramCount) { params[paramIndex++]= CharOperation.subarray(parametersSource, start, index); } if (paramIndex != paramCount) { // happens only for constructors with synthetic enclosing type in the signature System.arraycopy(params, 0, (params= new char[paramIndex][]), 0, paramIndex); } return params; } /* * @see IMethod */ public String[] getParameterTypes() { return this.parameterTypes; } public ITypeParameter getTypeParameter(String typeParameterName) { return new TypeParameter(this, typeParameterName); } public ITypeParameter[] getTypeParameters() throws JavaModelException { String[] typeParameterSignatures= getTypeParameterSignatures(); int length= typeParameterSignatures.length; if (length == 0) return TypeParameter.NO_TYPE_PARAMETERS; ITypeParameter[] typeParameters= new ITypeParameter[length]; for (int i= 0; i < typeParameterSignatures.length; i++) { String typeParameterName= Signature.getTypeVariable(typeParameterSignatures[i]); typeParameters[i]= new TypeParameter(this, typeParameterName); } return typeParameters; } /** * @see IMethod#getTypeParameterSignatures() * @since 3.0 * @deprecated */ public String[] getTypeParameterSignatures() throws JavaModelException { IBinaryMethod info= (IBinaryMethod)getElementInfo(); char[] genericSignature= info.getGenericSignature(); if (genericSignature == null) return CharOperation.NO_STRINGS; char[] dotBasedSignature= CharOperation.replaceOnCopy(genericSignature, '/', '.'); char[][] typeParams= Signature.getTypeParameters(dotBasedSignature); return CharOperation.toStrings(typeParams); } public String[] getRawParameterNames() throws JavaModelException { IBinaryMethod info= (IBinaryMethod)getElementInfo(); int paramCount= Signature.getParameterCount(new String(info.getMethodDescriptor())); return getRawParameterNames(paramCount); } private String[] getRawParameterNames(int paramCount) { String[] result= new String[paramCount]; for (int i= 0; i < paramCount; i++) { result[i]= "arg" + i; //$NON-NLS-1$ } return result; } /* * @see IMethod */ public String getReturnType() throws JavaModelException { if (this.returnType == null) { IBinaryMethod info= (IBinaryMethod)getElementInfo(); this.returnType= getReturnType(info); } return this.returnType; } private String getReturnType(IBinaryMethod info) { char[] genericSignature= info.getGenericSignature(); char[] signature= genericSignature == null ? info.getMethodDescriptor() : genericSignature; char[] dotBasedSignature= CharOperation.replaceOnCopy(signature, '/', '.'); String returnTypeName= Signature.getReturnType(new String(dotBasedSignature)); return new String(ClassFile.translatedName(returnTypeName.toCharArray())); } /* * @see IMethod */ public String getSignature() throws JavaModelException { IBinaryMethod info= (IBinaryMethod)getElementInfo(); return new String(info.getMethodDescriptor()); } /** * @see org.eclipse.jdt.internal.core.JavaElement#hashCode() */ public int hashCode() { int hash= super.hashCode(); for (int i= 0, length= this.parameterTypes.length; i < length; i++) { hash= Util.combineHashCodes(hash, this.parameterTypes[i].hashCode()); } return hash; } /* * @see IMethod */ public boolean isConstructor() throws JavaModelException { if (!getElementName().equals(this.parent.getElementName())) { // faster than reaching the info return false; } IBinaryMethod info= (IBinaryMethod)getElementInfo(); return info.isConstructor(); } /* * @see IMethod#isMainMethod() */ public boolean isMainMethod() throws JavaModelException { return this.isMainMethod(this); } /* (non-Javadoc) * @see org.eclipse.jdt.core.IMethod#isResolved() */ public boolean isResolved() { return false; } /* * @see IMethod#isSimilar(IMethod) */ public boolean isSimilar(IMethod method) { return areSimilarMethods( getElementName(), getParameterTypes(), method.getElementName(), method.getParameterTypes(), null); } public String readableName() { StringBuffer buffer= new StringBuffer(super.readableName()); buffer.append("("); //$NON-NLS-1$ String[] paramTypes= this.parameterTypes; int length; if (paramTypes != null && (length= paramTypes.length) > 0) { for (int i= 0; i < length; i++) { buffer.append(Signature.toString(paramTypes[i])); if (i < length - 1) { buffer.append(", "); //$NON-NLS-1$ } } } buffer.append(")"); //$NON-NLS-1$ return buffer.toString(); } public JavaElement resolved(Binding binding) { SourceRefElement resolvedHandle= new ResolvedBinaryMethod(this.parent, this.name, this.parameterTypes, new String(binding.computeUniqueKey())); resolvedHandle.occurrenceCount= this.occurrenceCount; return resolvedHandle; }/* * @private Debugging purposes */ protected void toStringInfo(int tab, StringBuffer buffer, Object info, boolean showResolvedInfo) { buffer.append(tabString(tab)); if (info == null) { toStringName(buffer); buffer.append(" (not open)"); //$NON-NLS-1$ } else if (info == NO_INFO) { toStringName(buffer); } else { IBinaryMethod methodInfo= (IBinaryMethod)info; int flags= methodInfo.getModifiers(); if (Flags.isStatic(flags)) { buffer.append("static "); //$NON-NLS-1$ } if (!methodInfo.isConstructor()) { buffer.append(Signature.toString(getReturnType(methodInfo))); buffer.append(' '); } toStringName(buffer, flags); } } protected void toStringName(StringBuffer buffer) { toStringName(buffer, 0); } protected void toStringName(StringBuffer buffer, int flags) { buffer.append(getElementName()); buffer.append('('); String[] parameters= getParameterTypes(); int length; if (parameters != null && (length= parameters.length) > 0) { boolean isVarargs= Flags.isVarargs(flags); for (int i= 0; i < length; i++) { try { if (i < length - 1) { buffer.append(Signature.toString(parameters[i])); buffer.append(", "); //$NON-NLS-1$ } else if (isVarargs) { // remove array from signature String parameter= parameters[i].substring(1); buffer.append(Signature.toString(parameter)); buffer.append(" ..."); //$NON-NLS-1$ } else { buffer.append(Signature.toString(parameters[i])); } } catch (IllegalArgumentException e) { // parameter signature is malformed buffer.append("*** invalid signature: "); //$NON-NLS-1$ buffer.append(parameters[i]); } } } buffer.append(')'); if (this.occurrenceCount > 1) { buffer.append("#"); //$NON-NLS-1$ buffer.append(this.occurrenceCount); } } public String getAttachedJavadoc(IProgressMonitor monitor) throws JavaModelException { JavadocContents javadocContents= ((BinaryType)this.getDeclaringType()).getJavadocContents(monitor); if (javadocContents == null) return null; return javadocContents.getMethodDoc(this); } }