/******************************************************************************* * 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.search.matching; import java.io.IOException; import org.eclipse.jdt.core.BindingKey; import org.eclipse.jdt.core.Flags; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.search.IJavaSearchConstants; import org.eclipse.jdt.core.search.SearchPattern; import org.eclipse.jdt.internal.compiler.ExtraFlags; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.core.index.EntryResult; import org.eclipse.jdt.internal.core.index.Index; import org.eclipse.jdt.internal.core.util.Util; public class ConstructorPattern extends JavaSearchPattern { protected boolean findDeclarations= true; protected boolean findReferences= true; public char[] declaringQualification; public char[] declaringSimpleName; public char[][] parameterQualifications; public char[][] parameterSimpleNames; public int parameterCount; public boolean varargs= false; // Signatures and arguments for generic search char[][][] parametersTypeSignatures; char[][][][] parametersTypeArguments; boolean constructorParameters= false; char[][] constructorArguments; protected static char[][] REF_CATEGORIES= { CONSTRUCTOR_REF }; protected static char[][] REF_AND_DECL_CATEGORIES= { CONSTRUCTOR_REF, CONSTRUCTOR_DECL }; protected static char[][] DECL_CATEGORIES= { CONSTRUCTOR_DECL }; public final static int FINE_GRAIN_MASK= IJavaSearchConstants.SUPER_REFERENCE | IJavaSearchConstants.QUALIFIED_REFERENCE | IJavaSearchConstants.THIS_REFERENCE | IJavaSearchConstants.IMPLICIT_THIS_REFERENCE; /** * Constructor entries are encoded as described * * Binary constructor for class TypeName '/' Arity '/' TypeModifers '/' PackageName '/' * Signature '/' ParameterNamesopt '/' Modifiers Source constructor for class TypeName '/' Arity * '/' TypeModifers '/' PackageName '/' ParameterTypes '/' ParameterNamesopt '/' Modifiers * Constructor with 0 arity for class TypeName '/' 0 '/' TypeModifers '/' PackageName '/' * Modifiers Constructor for enum, interface (annotation) and class with default constructor * TypeName '/' # '/' TypeModifers '/' PackageName Constructor for member type TypeName '/' * Arity '/' TypeModifers * * TypeModifiers contains some encoded extra information {@link ExtraFlags#IsMemberType} * {@link ExtraFlags#HasNonPrivateStaticMemberTypes} * {@link ExtraFlags#ParameterTypesStoredAsSignature} */ public static char[] createDeclarationIndexKey( char[] typeName, int argCount, char[] signature, char[][] parameterTypes, char[][] parameterNames, int modifiers, char[] packageName, int typeModifiers, int extraFlags) { char[] countChars; char[] parameterTypesChars= null; char[] parameterNamesChars= null; if (argCount < 0) { countChars= DEFAULT_CONSTRUCTOR; } else { countChars= argCount < 10 ? COUNTS[argCount] : ("/" + String.valueOf(argCount)).toCharArray(); //$NON-NLS-1$ if (argCount > 0) { if (signature == null) { if (parameterTypes != null && parameterTypes.length == argCount) { char[][] parameterTypeErasures= new char[argCount][]; for (int i= 0; i < parameterTypes.length; i++) { parameterTypeErasures[i]= getTypeErasure(parameterTypes[i]); } parameterTypesChars= CharOperation.concatWith(parameterTypeErasures, PARAMETER_SEPARATOR); } } else { extraFlags|= ExtraFlags.ParameterTypesStoredAsSignature; } if (parameterNames != null && parameterNames.length == argCount) { parameterNamesChars= CharOperation.concatWith(parameterNames, PARAMETER_SEPARATOR); } } } boolean isMemberType= (extraFlags & ExtraFlags.IsMemberType) != 0; int typeNameLength= typeName == null ? 0 : typeName.length; int packageNameLength= packageName == null ? 0 : packageName.length; int countCharsLength= countChars.length; int parameterTypesLength= signature == null ? (parameterTypesChars == null ? 0 : parameterTypesChars.length) : signature.length; int parameterNamesLength= parameterNamesChars == null ? 0 : parameterNamesChars.length; int resultLength= typeNameLength + countCharsLength + 3; // SEPARATOR=1 + TypeModifers=2 if (!isMemberType) { resultLength+= packageNameLength + 1; // SEPARATOR=1 if (argCount >= 0) { resultLength+= 3; // SEPARATOR=1 + Modifiers=2 } if (argCount > 0) { resultLength+= parameterTypesLength + parameterNamesLength + 2; //SEPARATOR=1 + SEPARATOR=1 } } char[] result= new char[resultLength]; int pos= 0; if (typeNameLength > 0) { System.arraycopy(typeName, 0, result, pos, typeNameLength); pos+= typeNameLength; } if (countCharsLength > 0) { System.arraycopy(countChars, 0, result, pos, countCharsLength); pos+= countCharsLength; } int typeModifiersWithExtraFlags= typeModifiers | encodeExtraFlags(extraFlags); result[pos++]= SEPARATOR; result[pos++]= (char)typeModifiersWithExtraFlags; result[pos++]= (char)(typeModifiersWithExtraFlags >> 16); if (!isMemberType) { result[pos++]= SEPARATOR; if (packageNameLength > 0) { System.arraycopy(packageName, 0, result, pos, packageNameLength); pos+= packageNameLength; } if (argCount == 0) { result[pos++]= SEPARATOR; result[pos++]= (char)modifiers; result[pos++]= (char)(modifiers >> 16); } else if (argCount > 0) { result[pos++]= SEPARATOR; if (parameterTypesLength > 0) { if (signature == null) { System.arraycopy(parameterTypesChars, 0, result, pos, parameterTypesLength); } else { System.arraycopy(CharOperation.replaceOnCopy(signature, SEPARATOR, '\\'), 0, result, pos, parameterTypesLength); } pos+= parameterTypesLength; } result[pos++]= SEPARATOR; if (parameterNamesLength > 0) { System.arraycopy(parameterNamesChars, 0, result, pos, parameterNamesLength); pos+= parameterNamesLength; } result[pos++]= SEPARATOR; result[pos++]= (char)modifiers; result[pos++]= (char)(modifiers >> 16); } } return result; } public static char[] createDefaultDeclarationIndexKey( char[] typeName, char[] packageName, int typeModifiers, int extraFlags) { return createDeclarationIndexKey( typeName, -1, // used to identify default constructor null, null, null, 0, // packageName, typeModifiers, extraFlags); } /** * Constructor entries are encoded as TypeName '/' Arity: e.g. 'X/0' */ public static char[] createIndexKey(char[] typeName, int argCount) { char[] countChars= argCount < 10 ? COUNTS[argCount] : ("/" + String.valueOf(argCount)).toCharArray(); //$NON-NLS-1$ return CharOperation.concat(typeName, countChars); } static int decodeExtraFlags(int modifiersWithExtraFlags) { int extraFlags= 0; if ((modifiersWithExtraFlags & ASTNode.Bit28) != 0) { extraFlags|= ExtraFlags.ParameterTypesStoredAsSignature; } if ((modifiersWithExtraFlags & ASTNode.Bit29) != 0) { extraFlags|= ExtraFlags.IsLocalType; } if ((modifiersWithExtraFlags & ASTNode.Bit30) != 0) { extraFlags|= ExtraFlags.IsMemberType; } if ((modifiersWithExtraFlags & ASTNode.Bit31) != 0) { extraFlags|= ExtraFlags.HasNonPrivateStaticMemberTypes; } return extraFlags; } static int decodeModifers(int modifiersWithExtraFlags) { return modifiersWithExtraFlags & ~(ASTNode.Bit31 | ASTNode.Bit30 | ASTNode.Bit29 | ASTNode.Bit28); } private static int encodeExtraFlags(int extraFlags) { int encodedExtraFlags= 0; if ((extraFlags & ExtraFlags.ParameterTypesStoredAsSignature) != 0) { encodedExtraFlags|= ASTNode.Bit28; } if ((extraFlags & ExtraFlags.IsLocalType) != 0) { encodedExtraFlags|= ASTNode.Bit29; } if ((extraFlags & ExtraFlags.IsMemberType) != 0) { encodedExtraFlags|= ASTNode.Bit30; } if ((extraFlags & ExtraFlags.HasNonPrivateStaticMemberTypes) != 0) { encodedExtraFlags|= ASTNode.Bit31; } return encodedExtraFlags; } private static char[] getTypeErasure(char[] typeName) { int index; if ((index= CharOperation.indexOf('<', typeName)) == -1) return typeName; int length= typeName.length; char[] typeErasurename= new char[length - 2]; System.arraycopy(typeName, 0, typeErasurename, 0, index); int depth= 1; for (int i= index + 1; i < length; i++) { switch (typeName[i]) { case '<': depth++; break; case '>': depth--; break; default: if (depth == 0) { typeErasurename[index++]= typeName[i]; } break; } } System.arraycopy(typeErasurename, 0, typeErasurename= new char[index], 0, index); return typeErasurename; } ConstructorPattern(int matchRule) { super(CONSTRUCTOR_PATTERN, matchRule); } public ConstructorPattern( char[] declaringSimpleName, char[] declaringQualification, char[][] parameterQualifications, char[][] parameterSimpleNames, int limitTo, int matchRule) { this(matchRule); this.fineGrain= limitTo & FINE_GRAIN_MASK; if (this.fineGrain == 0) { switch (limitTo) { case IJavaSearchConstants.DECLARATIONS: this.findReferences= false; break; case IJavaSearchConstants.REFERENCES: this.findDeclarations= false; break; case IJavaSearchConstants.ALL_OCCURRENCES: break; } } else { this.findDeclarations= false; } this.declaringQualification= this.isCaseSensitive ? declaringQualification : CharOperation.toLowerCase(declaringQualification); this.declaringSimpleName= (this.isCaseSensitive || this.isCamelCase) ? declaringSimpleName : CharOperation.toLowerCase(declaringSimpleName); if (parameterSimpleNames != null) { this.parameterCount= parameterSimpleNames.length; boolean synthetic= this.parameterCount > 0 && declaringQualification != null && CharOperation.equals(CharOperation.concat(parameterQualifications[0], parameterSimpleNames[0], '.'), declaringQualification); int offset= 0; if (synthetic) { // skip first synthetic parameter this.parameterCount--; offset++; } this.parameterQualifications= new char[this.parameterCount][]; this.parameterSimpleNames= new char[this.parameterCount][]; for (int i= 0; i < this.parameterCount; i++) { this.parameterQualifications[i]= this.isCaseSensitive ? parameterQualifications[i + offset] : CharOperation.toLowerCase(parameterQualifications[i + offset]); this.parameterSimpleNames[i]= this.isCaseSensitive ? parameterSimpleNames[i + offset] : CharOperation.toLowerCase(parameterSimpleNames[i + offset]); } } else { this.parameterCount= -1; } this.mustResolve= mustResolve(); } /* * Instanciate a method pattern with signatures for generics search */ public ConstructorPattern( char[] declaringSimpleName, char[] declaringQualification, char[][] parameterQualifications, char[][] parameterSimpleNames, String[] parameterSignatures, IMethod method, int limitTo, int matchRule) { this(declaringSimpleName, declaringQualification, parameterQualifications, parameterSimpleNames, limitTo, matchRule); // Set flags try { this.varargs= (method.getFlags() & Flags.AccVarargs) != 0; } catch (JavaModelException e) { // do nothing } // Get unique key for parameterized constructors String genericDeclaringTypeSignature= null; if (method.isResolved()) { String key= method.getKey(); BindingKey bindingKey= new BindingKey(key); if (bindingKey.isParameterizedType()) { genericDeclaringTypeSignature= Util.getDeclaringTypeSignature(key); // Store type signature and arguments for declaring type if (genericDeclaringTypeSignature != null) { this.typeSignatures= Util.splitTypeLevelsSignature(genericDeclaringTypeSignature); setTypeArguments(Util.getAllTypeArguments(this.typeSignatures)); } } } else { this.constructorParameters= true; storeTypeSignaturesAndArguments(method.getDeclaringType()); } // store type signatures and arguments for method parameters type if (parameterSignatures != null) { int length= parameterSignatures.length; if (length > 0) { this.parametersTypeSignatures= new char[length][][]; this.parametersTypeArguments= new char[length][][][]; for (int i= 0; i < length; i++) { this.parametersTypeSignatures[i]= Util.splitTypeLevelsSignature(parameterSignatures[i]); this.parametersTypeArguments[i]= Util.getAllTypeArguments(this.parametersTypeSignatures[i]); } } } // Store type signatures and arguments for method this.constructorArguments= extractMethodArguments(method); if (hasConstructorArguments()) this.mustResolve= true; } /* * Instanciate a method pattern with signatures for generics search */ public ConstructorPattern( char[] declaringSimpleName, char[] declaringQualification, String declaringSignature, char[][] parameterQualifications, char[][] parameterSimpleNames, String[] parameterSignatures, char[][] arguments, int limitTo, int matchRule) { this(declaringSimpleName, declaringQualification, parameterQualifications, parameterSimpleNames, limitTo, matchRule); // Store type signature and arguments for declaring type if (declaringSignature != null) { this.typeSignatures= Util.splitTypeLevelsSignature(declaringSignature); setTypeArguments(Util.getAllTypeArguments(this.typeSignatures)); } // Store type signatures and arguments for method parameters type if (parameterSignatures != null) { int length= parameterSignatures.length; if (length > 0) { this.parametersTypeSignatures= new char[length][][]; this.parametersTypeArguments= new char[length][][][]; for (int i= 0; i < length; i++) { this.parametersTypeSignatures[i]= Util.splitTypeLevelsSignature(parameterSignatures[i]); this.parametersTypeArguments[i]= Util.getAllTypeArguments(this.parametersTypeSignatures[i]); } } } // Store type signatures and arguments for method this.constructorArguments= arguments; if (arguments == null || arguments.length == 0) { if (getTypeArguments() != null && getTypeArguments().length > 0) { this.constructorArguments= getTypeArguments()[0]; } } if (hasConstructorArguments()) this.mustResolve= true; } public void decodeIndexKey(char[] key) { int last= key.length - 1; int slash= CharOperation.indexOf(SEPARATOR, key, 0); this.declaringSimpleName= CharOperation.subarray(key, 0, slash); int start= slash + 1; slash= CharOperation.indexOf(SEPARATOR, key, start); if (slash != -1) { last= slash - 1; } boolean isDefaultConstructor= key[last] == '#'; if (isDefaultConstructor) { this.parameterCount= -1; } else { this.parameterCount= 0; int power= 1; for (int i= last; i >= start; i--) { if (i == last) { this.parameterCount= key[i] - '0'; } else { power*= 10; this.parameterCount+= power * (key[i] - '0'); } } } } public SearchPattern getBlankPattern() { return new ConstructorPattern(R_EXACT_MATCH | R_CASE_SENSITIVE); } public char[][] getIndexCategories() { if (this.findReferences) return this.findDeclarations ? REF_AND_DECL_CATEGORIES : REF_CATEGORIES; if (this.findDeclarations) return DECL_CATEGORIES; return CharOperation.NO_CHAR_CHAR; } boolean hasConstructorArguments() { return this.constructorArguments != null && this.constructorArguments.length > 0; } boolean hasConstructorParameters() { return this.constructorParameters; } public boolean matchesDecodedKey(SearchPattern decodedPattern) { ConstructorPattern pattern= (ConstructorPattern)decodedPattern; return pattern.parameterCount != -1 && (this.parameterCount == pattern.parameterCount || this.parameterCount == -1 || this.varargs) && matchesName(this.declaringSimpleName, pattern.declaringSimpleName); } protected boolean mustResolve() { if (this.declaringQualification != null) return true; // parameter types if (this.parameterSimpleNames != null) for (int i= 0, max= this.parameterSimpleNames.length; i < max; i++) if (this.parameterQualifications[i] != null) return true; return this.findReferences; // need to check resolved default constructors and explicit constructor calls } public EntryResult[] queryIn(Index index) throws IOException { char[] key= this.declaringSimpleName; // can be null int matchRule= getMatchRule(); switch (getMatchMode()) { case R_EXACT_MATCH: if (this.declaringSimpleName != null && this.parameterCount >= 0 && !this.varargs) { key= createIndexKey(this.declaringSimpleName, this.parameterCount); } matchRule&= ~R_EXACT_MATCH; matchRule|= R_PREFIX_MATCH; break; case R_PREFIX_MATCH: // do a prefix query with the declaringSimpleName break; case R_PATTERN_MATCH: if (this.parameterCount >= 0 && !this.varargs) { key= CharOperation.concat(createIndexKey(this.declaringSimpleName == null ? ONE_STAR : this.declaringSimpleName, this.parameterCount), ONE_STAR); } else if (this.declaringSimpleName != null && this.declaringSimpleName[this.declaringSimpleName.length - 1] != '*') { key= CharOperation.concat(this.declaringSimpleName, ONE_STAR, SEPARATOR); } else if (key != null) { key= CharOperation.concat(key, ONE_STAR); } // else do a pattern query with just the declaringSimpleName break; case R_REGEXP_MATCH: // TODO (frederic) implement regular expression match break; case R_CAMELCASE_MATCH: case R_CAMELCASE_SAME_PART_COUNT_MATCH: // do a prefix query with the declaringSimpleName break; } return index.query(getIndexCategories(), key, matchRule); // match rule is irrelevant when the key is null } protected StringBuffer print(StringBuffer output) { if (this.findDeclarations) { output.append(this.findReferences ? "ConstructorCombinedPattern: " //$NON-NLS-1$ : "ConstructorDeclarationPattern: "); //$NON-NLS-1$ } else { output.append("ConstructorReferencePattern: "); //$NON-NLS-1$ } if (this.declaringQualification != null) output.append(this.declaringQualification).append('.'); if (this.declaringSimpleName != null) output.append(this.declaringSimpleName); else if (this.declaringQualification != null) output.append("*"); //$NON-NLS-1$ output.append('('); if (this.parameterSimpleNames == null) { output.append("..."); //$NON-NLS-1$ } else { for (int i= 0, max= this.parameterSimpleNames.length; i < max; i++) { if (i > 0) output.append(", "); //$NON-NLS-1$ if (this.parameterQualifications[i] != null) output.append(this.parameterQualifications[i]).append('.'); if (this.parameterSimpleNames[i] == null) output.append('*'); else output.append(this.parameterSimpleNames[i]); } } output.append(')'); return super.print(output); } }