| /******************************************************************************* |
| * Copyright (c) 2000, 2014 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * 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 | |
| IJavaSearchConstants.METHOD_REFERENCE_EXPRESSION; |
| |
| |
| /** |
| * 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(); |
| } |
| /* |
| * Instantiate 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; |
| } |
| /* |
| * Instantiate 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; |
| } |
| |
| @Override |
| 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'); |
| } |
| } |
| } |
| } |
| @Override |
| public SearchPattern getBlankPattern() { |
| return new ConstructorPattern(R_EXACT_MATCH | R_CASE_SENSITIVE); |
| } |
| @Override |
| 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; |
| } |
| @Override |
| 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 |
| } |
| @Override |
| 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 |
| } |
| @Override |
| 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); |
| } |
| } |