blob: ea70f404d265cc8abc69f5f43d6826400ad1c4b4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2004 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 org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.*;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.search.*;
import org.eclipse.jdt.internal.compiler.env.*;
import org.eclipse.jdt.internal.compiler.lookup.*;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
import org.eclipse.jdt.internal.core.*;
import org.eclipse.jdt.internal.core.search.indexing.IIndexConstants;
public class ClassFileMatchLocator implements IIndexConstants {
public static char[] convertClassFileFormat(char[] name) {
return CharOperation.replaceOnCopy(name, '/', '.');
}
boolean checkDeclaringType(IBinaryType enclosingBinaryType, char[] simpleName, char[] qualification, boolean isCaseSensitive) {
if (simpleName == null && qualification == null) return true;
if (enclosingBinaryType == null) return true;
char[] declaringTypeName = convertClassFileFormat(enclosingBinaryType.getName());
return checkTypeName(simpleName, qualification, declaringTypeName, isCaseSensitive);
}
boolean checkParameters(char[] methodDescriptor, char[][] parameterSimpleNames, char[][] parameterQualifications, boolean isCaseSensitive) {
char[][] arguments = Signature.getParameterTypes(methodDescriptor);
int parameterCount = parameterSimpleNames.length;
if (parameterCount != arguments.length) return false;
for (int i = 0; i < parameterCount; i++)
if (!checkTypeName(parameterSimpleNames[i], parameterQualifications[i], Signature.toCharArray(arguments[i]), isCaseSensitive))
return false;
return true;
}
boolean checkTypeName(char[] simpleName, char[] qualification, char[] fullyQualifiedTypeName, boolean isCaseSensitive) {
// NOTE: if case insensitive then simpleName & qualification are assumed to be lowercase
char[] wildcardPattern = PatternLocator.qualifiedPattern(simpleName, qualification);
if (wildcardPattern == null) return true;
return CharOperation.match(wildcardPattern, fullyQualifiedTypeName, isCaseSensitive);
}
/**
* Locate declaration in the current class file. This class file is always in a jar.
*/
public void locateMatches(MatchLocator locator, ClassFile classFile, IBinaryType info) throws CoreException {
// check class definition
SearchPattern pattern = locator.pattern;
BinaryType binaryType = (BinaryType) classFile.getType();
if (matchBinary(pattern, info, null)) {
binaryType = new ResolvedBinaryType((JavaElement) binaryType.getParent(), binaryType.getElementName(), binaryType.getKey());
locator.reportBinaryMemberDeclaration(null, binaryType, null, info, SearchMatch.A_ACCURATE);
}
int accuracy = SearchMatch.A_ACCURATE;
if (((InternalSearchPattern)pattern).mustResolve) {
try {
BinaryTypeBinding binding = locator.cacheBinaryType(binaryType, info);
if (binding != null) {
// filter out element not in hierarchy scope
if (!locator.typeInHierarchy(binding)) return;
MethodBinding[] methods = binding.methods();
for (int i = 0, l = methods.length; i < l; i++) {
MethodBinding method = methods[i];
if (locator.patternLocator.resolveLevel(method) == PatternLocator.ACCURATE_MATCH) {
IMethod methodHandle = binaryType.getMethod(
new String(method.isConstructor() ? binding.compoundName[binding.compoundName.length-1] : method.selector),
CharOperation.toStrings(Signature.getParameterTypes(convertClassFileFormat(method.signature()))));
locator.reportBinaryMemberDeclaration(null, methodHandle, method, info, SearchMatch.A_ACCURATE);
}
}
FieldBinding[] fields = binding.fields();
for (int i = 0, l = fields.length; i < l; i++) {
FieldBinding field = fields[i];
if (locator.patternLocator.resolveLevel(field) == PatternLocator.ACCURATE_MATCH) {
IField fieldHandle = binaryType.getField(new String(field.name));
locator.reportBinaryMemberDeclaration(null, fieldHandle, field, info, SearchMatch.A_ACCURATE);
}
}
// no need to check binary info since resolve was successful
return;
}
} catch (AbortCompilation e) { // if compilation was aborted it is a problem with the class path
}
// report as a potential match if binary info matches the pattern
accuracy = SearchMatch.A_INACCURATE;
}
IBinaryMethod[] methods = info.getMethods();
if (methods != null) {
for (int i = 0, l = methods.length; i < l; i++) {
IBinaryMethod method = methods[i];
if (matchBinary(pattern, method, info)) {
char[] name;
if (method.isConstructor()) {
name = info.getName();
int lastSlash = CharOperation.lastIndexOf('/', name);
if (lastSlash != -1) {
name = CharOperation.subarray(name, lastSlash+1, name.length);
}
} else {
name = method.getSelector();
}
String selector = new String(name);
String[] parameterTypes = CharOperation.toStrings(Signature.getParameterTypes(convertClassFileFormat(method.getMethodDescriptor())));
IMethod methodHandle = binaryType.getMethod(selector, parameterTypes);
methodHandle = new ResolvedBinaryMethod(binaryType, selector, parameterTypes, methodHandle.getKey());
locator.reportBinaryMemberDeclaration(null, methodHandle, null, info, accuracy);
}
}
}
IBinaryField[] fields = info.getFields();
if (fields != null) {
for (int i = 0, l = fields.length; i < l; i++) {
IBinaryField field = fields[i];
if (matchBinary(pattern, field, info)) {
String fieldName = new String(field.getName());
IField fieldHandle = binaryType.getField(fieldName);
fieldHandle = new ResolvedBinaryField(binaryType, fieldName, fieldHandle.getKey());
locator.reportBinaryMemberDeclaration(null, fieldHandle, null, info, accuracy);
}
}
}
}
/**
* Finds out whether the given binary info matches the search pattern.
* Default is to return false.
*/
boolean matchBinary(SearchPattern pattern, Object binaryInfo, IBinaryType enclosingBinaryType) {
switch (((InternalSearchPattern)pattern).kind) {
case CONSTRUCTOR_PATTERN :
return matchConstructor((ConstructorPattern) pattern, binaryInfo, enclosingBinaryType);
case FIELD_PATTERN :
return matchField((FieldPattern) pattern, binaryInfo, enclosingBinaryType);
case METHOD_PATTERN :
return matchMethod((MethodPattern) pattern, binaryInfo, enclosingBinaryType);
case SUPER_REF_PATTERN :
return matchSuperTypeReference((SuperTypeReferencePattern) pattern, binaryInfo, enclosingBinaryType);
case TYPE_DECL_PATTERN :
return matchTypeDeclaration((TypeDeclarationPattern) pattern, binaryInfo, enclosingBinaryType);
case OR_PATTERN :
SearchPattern[] patterns = ((OrPattern) pattern).patterns;
for (int i = 0, length = patterns.length; i < length; i++)
if (matchBinary(patterns[i], binaryInfo, enclosingBinaryType)) return true;
}
return false;
}
boolean matchConstructor(ConstructorPattern pattern, Object binaryInfo, IBinaryType enclosingBinaryType) {
if (!pattern.findDeclarations) return false; // only relevant when finding declarations
if (!(binaryInfo instanceof IBinaryMethod)) return false;
IBinaryMethod method = (IBinaryMethod) binaryInfo;
if (!method.isConstructor()) return false;
if (!checkDeclaringType(enclosingBinaryType, pattern.declaringSimpleName, pattern.declaringQualification, pattern.isCaseSensitive()))
return false;
if (pattern.parameterSimpleNames != null) {
char[] methodDescriptor = convertClassFileFormat(method.getMethodDescriptor());
if (!checkParameters(methodDescriptor, pattern.parameterSimpleNames, pattern.parameterQualifications, pattern.isCaseSensitive()))
return false;
}
return true;
}
boolean matchField(FieldPattern pattern, Object binaryInfo, IBinaryType enclosingBinaryType) {
if (!pattern.findDeclarations) return false; // only relevant when finding declarations
if (!(binaryInfo instanceof IBinaryField)) return false;
IBinaryField field = (IBinaryField) binaryInfo;
if (!pattern.matchesName(pattern.name, field.getName())) return false;
if (!checkDeclaringType(enclosingBinaryType, pattern.declaringSimpleName, pattern.declaringQualification, pattern.isCaseSensitive()))
return false;
char[] fieldTypeSignature = Signature.toCharArray(convertClassFileFormat(field.getTypeName()));
return checkTypeName(pattern.typeSimpleName, pattern.typeQualification, fieldTypeSignature, pattern.isCaseSensitive());
}
boolean matchMethod(MethodPattern pattern, Object binaryInfo, IBinaryType enclosingBinaryType) {
if (!pattern.findDeclarations) return false; // only relevant when finding declarations
if (!(binaryInfo instanceof IBinaryMethod)) return false;
IBinaryMethod method = (IBinaryMethod) binaryInfo;
if (!pattern.matchesName(pattern.selector, method.getSelector())) return false;
if (!checkDeclaringType(enclosingBinaryType, pattern.declaringSimpleName, pattern.declaringQualification, pattern.isCaseSensitive()))
return false;
// look at return type only if declaring type is not specified
boolean checkReturnType = pattern.declaringSimpleName == null && (pattern.returnSimpleName != null || pattern.returnQualification != null);
boolean checkParameters = pattern.parameterSimpleNames != null;
if (checkReturnType || checkParameters) {
char[] methodDescriptor = convertClassFileFormat(method.getMethodDescriptor());
if (checkReturnType) {
char[] returnTypeSignature = Signature.toCharArray(Signature.getReturnType(methodDescriptor));
if (!checkTypeName(pattern.returnSimpleName, pattern.returnQualification, returnTypeSignature, pattern.isCaseSensitive()))
return false;
}
if (checkParameters && !checkParameters(methodDescriptor, pattern.parameterSimpleNames, pattern.parameterQualifications, pattern.isCaseSensitive()))
return false;
}
return true;
}
boolean matchSuperTypeReference(SuperTypeReferencePattern pattern, Object binaryInfo, IBinaryType enclosingBinaryType) {
if (!(binaryInfo instanceof IBinaryType)) return false;
IBinaryType type = (IBinaryType) binaryInfo;
if (!pattern.checkOnlySuperinterfaces) {
char[] vmName = type.getSuperclassName();
if (vmName != null) {
char[] superclassName = convertClassFileFormat(vmName);
if (checkTypeName(pattern.superSimpleName, pattern.superQualification, superclassName, pattern.isCaseSensitive()))
return true;
}
}
char[][] superInterfaces = type.getInterfaceNames();
if (superInterfaces != null) {
for (int i = 0, max = superInterfaces.length; i < max; i++) {
char[] superInterfaceName = convertClassFileFormat(superInterfaces[i]);
if (checkTypeName(pattern.superSimpleName, pattern.superQualification, superInterfaceName, pattern.isCaseSensitive()))
return true;
}
}
return false;
}
boolean matchTypeDeclaration(TypeDeclarationPattern pattern, Object binaryInfo, IBinaryType enclosingBinaryType) {
if (!(binaryInfo instanceof IBinaryType)) return false;
IBinaryType type = (IBinaryType) binaryInfo;
char[] fullyQualifiedTypeName = convertClassFileFormat(type.getName());
if (pattern.enclosingTypeNames == null || pattern instanceof QualifiedTypeDeclarationPattern) {
if (!checkTypeName(pattern.simpleName, pattern.pkg, fullyQualifiedTypeName, pattern.isCaseSensitive())) return false;
} else {
char[] enclosingTypeName = CharOperation.concatWith(pattern.enclosingTypeNames, '.');
char[] patternString = pattern.pkg == null
? enclosingTypeName
: CharOperation.concat(pattern.pkg, enclosingTypeName, '.');
if (!checkTypeName(pattern.simpleName, patternString, fullyQualifiedTypeName, pattern.isCaseSensitive())) return false;
}
switch (pattern.typeSuffix) {
case CLASS_SUFFIX:
return type.getKind() == IGenericType.CLASS_DECL;
case INTERFACE_SUFFIX:
return type.getKind() == IGenericType.INTERFACE_DECL;
case ENUM_SUFFIX:
return type.getKind() == IGenericType.ENUM_DECL;
case ANNOTATION_TYPE_SUFFIX:
return type.getKind() == IGenericType.ANNOTATION_TYPE_DECL;
case TYPE_SUFFIX: // nothing
}
return true;
}
}