blob: 1192314b0a2a0b8140d71ef10bd20b5f34f93cde [file] [log] [blame]
/*******************************************************************************
* 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);
}
}