blob: ee2dd4f09b60c352456a67b91e87a35ee018b001 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.core.search.matching;
import java.io.IOException;
import java.util.HashMap;
import java.util.zip.ZipFile;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.jdt.core.*;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.InvalidInputException;
import org.eclipse.jdt.core.search.IJavaSearchResultCollector;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
import org.eclipse.jdt.internal.compiler.ast.*;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
import org.eclipse.jdt.internal.compiler.env.*;
import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.impl.ITypeRequestor;
import org.eclipse.jdt.internal.compiler.lookup.*;
import org.eclipse.jdt.internal.compiler.parser.Scanner;
import org.eclipse.jdt.internal.compiler.parser.SourceTypeConverter;
import org.eclipse.jdt.internal.compiler.parser.TerminalTokens;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
import org.eclipse.jdt.internal.compiler.util.HashtableOfObject;
import org.eclipse.jdt.internal.core.*;
import org.eclipse.jdt.internal.core.hierarchy.HierarchyResolver;
import org.eclipse.jdt.internal.core.search.HierarchyScope;
/**
* Locate matches in compilation units.
*/
public class MatchLocator implements ITypeRequestor {
public SearchPattern pattern;
public int detailLevel;
public IJavaSearchResultCollector collector;
public IJavaSearchScope scope;
public MatchLocatorParser parser;
public INameEnvironment nameEnvironment;
public NameLookup nameLookup;
public LookupEnvironment lookupEnvironment;
public HashtableOfObject parsedUnits;
public MatchingOpenableSet matchingOpenables;
private MatchingOpenable currentMatchingOpenable;
public HandleFactory handleFactory;
public IWorkingCopy[] workingCopies;
public HierarchyResolver hierarchyResolver;
public IProgressMonitor progressMonitor;
final static char[] EMPTY_FILE_NAME = CharOperation.NO_CHAR;
boolean compilationAborted;
public MatchLocator(
SearchPattern pattern,
int detailLevel,
IJavaSearchResultCollector collector,
IJavaSearchScope scope,
IProgressMonitor progressMonitor) {
this.pattern = pattern;
this.detailLevel = detailLevel;
this.collector = collector;
this.scope = scope;
this.progressMonitor = progressMonitor;
}
/**
* Add an additional binary type
*/
public void accept(IBinaryType binaryType, PackageBinding packageBinding) {
BinaryTypeBinding binaryBinding = new BinaryTypeBinding(packageBinding, binaryType, this.lookupEnvironment);
ReferenceBinding cachedType = this.lookupEnvironment.getCachedType(binaryBinding.compoundName);
if (cachedType == null || cachedType instanceof UnresolvedReferenceBinding) { // NB: cachedType is not null if already cached as a source type
this.lookupEnvironment.createBinaryTypeFrom(binaryType, packageBinding);
}
}
/**
* Add an additional compilation unit.
*/
public void accept(ICompilationUnit sourceUnit) {
// diet parse
IFile file = ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(new Path(new String(sourceUnit.getFileName())));
CompilationUnit compilationUnit = (CompilationUnit)JavaCore.create(file);
CompilationUnitDeclaration parsedUnit = this.parser.dietParse(sourceUnit, this, file, compilationUnit);
// build bindings
this.lookupEnvironment.buildTypeBindings(parsedUnit);
this.lookupEnvironment.completeTypeBindings(parsedUnit, true);
// remember parsed unit
ImportReference pkg = parsedUnit.currentPackage;
char[][] packageName = pkg == null ? null : pkg.tokens;
char[] mainTypeName = sourceUnit.getMainTypeName();
char[] qualifiedName = packageName == null ? mainTypeName : CharOperation.concatWith(packageName, mainTypeName, '.');
this.parsedUnits.put(qualifiedName, parsedUnit);
}
/**
* Add an additional source type
*/
public void accept(ISourceType[] sourceTypes, PackageBinding packageBinding) {
ISourceType sourceType = sourceTypes[0];
while (sourceType.getEnclosingType() != null)
sourceType = sourceType.getEnclosingType();
if (sourceType instanceof SourceTypeElementInfo) {
// get source
SourceTypeElementInfo elementInfo = (SourceTypeElementInfo) sourceType;
IType type = elementInfo.getHandle();
ICompilationUnit sourceUnit = (ICompilationUnit)type.getCompilationUnit();
this.accept(sourceUnit);
} else {
CompilationResult result =
new CompilationResult(sourceType.getFileName(), 0, 0, 0);
CompilationUnitDeclaration unit =
SourceTypeConverter.buildCompilationUnit(
sourceTypes,//sourceTypes[0] is always toplevel here
true, // need field and methods
true, // need member types
false, // no need for field initialization
lookupEnvironment.problemReporter,
result);
this.lookupEnvironment.buildTypeBindings(unit);
this.lookupEnvironment.completeTypeBindings(unit, true);
this.parsedUnits.put(sourceType.getQualifiedName(), unit);
}
}
/**
* Creates an IField from the given field declaration and type.
*/
public IField createFieldHandle(
FieldDeclaration field,
IType type) {
if (type == null) return null;
return type.getField(new String(field.name));
}
/**
* Creates an IImportDeclaration from the given import statement
*/
public IJavaElement createImportHandle(ImportReference importRef) {
char[] importName = CharOperation.concatWith(importRef.getImportName(), '.');
if (importRef.onDemand) {
importName = CharOperation.concat(importName, ".*" .toCharArray()); //$NON-NLS-1$
}
Openable currentOpenable = this.getCurrentOpenable();
if (currentOpenable instanceof CompilationUnit) {
return ((CompilationUnit)currentOpenable).getImport(
new String(importName));
} else {
try {
return ((org.eclipse.jdt.internal.core.ClassFile)currentOpenable).getType();
} catch (JavaModelException e) {
return null;
}
}
}
/**
* Creates an IInitializer from the given field declaration and type.
*/
public IInitializer createInitializerHandle(
TypeDeclaration typeDecl,
FieldDeclaration initializer,
IType type) {
if (type == null) return null;
// find occurence count of the given initializer in its type declaration
int occurrenceCount = 0;
FieldDeclaration[] fields = typeDecl.fields;
for (int i = 0, length = fields.length; i < length; i++) {
FieldDeclaration field = fields[i];
if (!field.isField()) {
occurrenceCount++;
if (field.equals(initializer)) {
break;
}
}
}
return type.getInitializer(occurrenceCount);
}
/**
* Creates an IMethod from the given method declaration and type.
*/
public IMethod createMethodHandle(
AbstractMethodDeclaration method,
IType type) {
if (type == null) return null;
Argument[] arguments = method.arguments;
int length = arguments == null ? 0 : arguments.length;
if (type.isBinary()) {
// don't cache the methods of the binary type
ClassFileReader reader = this.classFileReader(type);
if (reader == null) return null;
IBinaryMethod[] methods = reader.getMethods();
if (methods != null) {
for (int i = 0, methodsLength = methods.length; i < methodsLength; i++) {
IBinaryMethod binaryMethod = methods[i];
char[] selector = binaryMethod.isConstructor() ? type.getElementName().toCharArray() : binaryMethod.getSelector();
if (CharOperation.equals(selector, method.selector)) {
String[] parameterTypes = Signature.getParameterTypes(new String(binaryMethod.getMethodDescriptor()));
if (length != parameterTypes.length) continue;
boolean sameParameters = true;
for (int j = 0; j < length; j++) {
TypeReference parameterType = arguments[j].type;
char[] typeName = CharOperation.concatWith(parameterType.getTypeName(), '.');
for (int k = 0; k < parameterType.dimensions(); k++) {
typeName = CharOperation.concat(typeName, "[]" .toCharArray()); //$NON-NLS-1$
}
String parameterTypeName = parameterTypes[j].replace('/', '.');
if (!Signature.toString(parameterTypeName).endsWith(new String(typeName))) {
sameParameters = false;
break;
} else {
parameterTypes[j] = parameterTypeName;
}
}
if (sameParameters) {
return type.getMethod(new String(selector), parameterTypes);
}
}
}
}
return null;
} else {
String[] parameterTypeSignatures = new String[length];
for (int i = 0; i < length; i++) {
TypeReference parameterType = arguments[i].type;
char[] typeName = CharOperation.concatWith(parameterType.getTypeName(), '.');
for (int j = 0; j < parameterType.dimensions(); j++) {
typeName = CharOperation.concat(typeName, "[]" .toCharArray()); //$NON-NLS-1$
}
parameterTypeSignatures[i] = Signature.createTypeSignature(typeName, false);
}
return type.getMethod(new String(method.selector), parameterTypeSignatures);
}
}
private ClassFileReader classFileReader(IType type) {
IClassFile classFile = type.getClassFile();
if (((IOpenable)classFile).isOpen()) {
JavaModelManager manager = JavaModelManager.getJavaModelManager();
synchronized(manager){
return (ClassFileReader)manager.getInfo(type);
}
} else {
IPackageFragment pkg = type.getPackageFragment();
IPackageFragmentRoot root = (IPackageFragmentRoot)pkg.getParent();
try {
if (root.isArchive()) {
IPath zipPath = root.isExternal() ? root.getPath() : root.getResource().getLocation();
if (zipPath == null) return null; // location is null
ZipFile zipFile = null;
try {
if (JavaModelManager.ZIP_ACCESS_VERBOSE) {
System.out.println("(" + Thread.currentThread() + ") [MatchLocator.classFileReader()] Creating ZipFile on " + zipPath); //$NON-NLS-1$ //$NON-NLS-2$
}
zipFile = new ZipFile(zipPath.toOSString());
char[] pkgPath = pkg.getElementName().toCharArray();
CharOperation.replace(pkgPath, '.', '/');
char[] classFileName = classFile.getElementName().toCharArray();
char[] path = pkgPath.length == 0 ? classFileName : CharOperation.concat(pkgPath, classFileName, '/');
return ClassFileReader.read(zipFile, new String(path));
} finally {
if (zipFile != null) {
try {
zipFile.close();
} catch (IOException e) {
}
}
}
} else {
return ClassFileReader.read(type.getPath().toOSString());
}
} catch (ClassFormatException e) {
return null;
} catch (IOException e) {
return null;
}
}
}
/**
* Creates an IType from the given simple top level type name.
*/
public IType createTypeHandle(char[] simpleTypeName) {
Openable currentOpenable = this.getCurrentOpenable();
if (currentOpenable instanceof CompilationUnit) {
// creates compilation unit
CompilationUnit unit = (CompilationUnit)currentOpenable;
// create type
return unit.getType(new String(simpleTypeName));
} else {
IType type;
try {
type = ((org.eclipse.jdt.internal.core.ClassFile)currentOpenable).getType();
} catch (JavaModelException e) {
return null;
}
// ensure this is a top level type (see bug 20011 Searching for Inner Classes gives bad search results)
return MatchingOpenable.getTopLevelType(type);
}
}
/**
* Creates an IType from the given simple inner type name and parent type.
*/
public IType createTypeHandle(IType parent, char[] simpleTypeName) {
return parent.getType(new String(simpleTypeName));
}
protected IResource getCurrentResource() {
return this.currentMatchingOpenable.resource;
}
protected Scanner getScanner() {
return this.parser == null ? null : this.parser.scanner;
}
/**
* Create a new parser for the given project, as well as a lookup environment.
*/
public void initialize(JavaProject project) throws JavaModelException {
// create lookup environment
CompilerOptions options = new CompilerOptions(project.getOptions(true));
ProblemReporter problemReporter =
new ProblemReporter(
DefaultErrorHandlingPolicies.proceedWithAllProblems(),
options,
new DefaultProblemFactory());
this.lookupEnvironment =
new LookupEnvironment(this, options, problemReporter, this.nameEnvironment);
// create parser
this.parser = new MatchLocatorParser(problemReporter, options.sourceLevel >= CompilerOptions.JDK1_4);
// reset parsed units (they could hold onto obsolete bindings: see bug 16052)
MatchingOpenable[] openables = this.matchingOpenables.getMatchingOpenables(project.getPackageFragmentRoots());
for (int i = 0, length = openables.length; i < length; i++) {
MatchingOpenable matchingOpenable = openables[i];
matchingOpenable.reset();
}
this.parsedUnits = new HashtableOfObject(10);
// remember project's name lookup
this.nameLookup = project.getNameLookup();
}
public void initializeNameEnvironment(JavaProject project) throws JavaModelException {
// cleanup and recreate file name environment
if (this.nameEnvironment != null) {
this.nameEnvironment.cleanup();
}
this.nameEnvironment = this.getNameEnvironment(project);
}
/**
* Locate the matches in the given files and report them using the search requestor.
*/
public void locateMatches(
String[] filePaths,
IWorkspace workspace,
IWorkingCopy[] workingCopies)
throws JavaModelException {
if (SearchEngine.VERBOSE) {
System.out.println("Locating matches in files ["); //$NON-NLS-1$
for (int i = 0, length = filePaths.length; i < length; i++) {
String path = filePaths[i];
System.out.println("\t" + path); //$NON-NLS-1$
}
System.out.println("]"); //$NON-NLS-1$
if (workingCopies != null) {
System.out.println(" and working copies ["); //$NON-NLS-1$
for (int i = 0, length = workingCopies.length; i < length; i++) {
IWorkingCopy wc = workingCopies[i];
System.out.println("\t" + ((JavaElement)wc).toStringWithAncestors()); //$NON-NLS-1$
}
System.out.println("]"); //$NON-NLS-1$
}
}
JavaModelManager manager = JavaModelManager.getJavaModelManager();
try {
// optimize access to zip files during search operation
manager.cacheZipFiles();
// initialize handle factory (used as a cache of handles so as to optimize space)
if (this.handleFactory == null) {
this.handleFactory = new HandleFactory(workspace);
}
// initialize locator with working copies
this.workingCopies = workingCopies;
// substitute compilation units with working copies
HashMap wcPaths = new HashMap(); // a map from path to working copies
int wcLength;
if (workingCopies != null && (wcLength = workingCopies.length) > 0) {
String[] newPaths = new String[wcLength];
for (int i = 0; i < wcLength; i++) {
IWorkingCopy workingCopy = workingCopies[i];
String path = workingCopy.getOriginalElement().getPath().toString();
wcPaths.put(path, workingCopy);
newPaths[i] = path;
}
int filePathsLength = filePaths.length;
System.arraycopy(filePaths, 0, filePaths = new String[filePathsLength+wcLength], 0, filePathsLength);
System.arraycopy(newPaths, 0, filePaths, filePathsLength, wcLength);
}
int length = filePaths.length;
if (progressMonitor != null) {
if (this.pattern.needsResolve) {
progressMonitor.beginTask("", length * 10); // 1 for file path, 3 for parsing, 6 for binding resolution //$NON-NLS-1$
} else {
progressMonitor.beginTask("", length * 4); // 1 for file path, 3 for parsing //$NON-NLS-1$
}
}
// sort file paths projects
Util.sort(filePaths);
// initialize pattern for polymorphic search (ie. method reference pattern)
this.matchingOpenables = new MatchingOpenableSet();
this.pattern.initializePolymorphicSearch(this, progressMonitor);
JavaProject previousJavaProject = null;
for (int i = 0; i < length; i++) {
if (progressMonitor != null && progressMonitor.isCanceled()) {
throw new OperationCanceledException();
}
String pathString = filePaths[i];
// skip duplicate paths
if (i > 0 && pathString.equals(filePaths[i-1])) continue;
Openable openable;
IWorkingCopy workingCopy = (IWorkingCopy)wcPaths.get(pathString);
if (workingCopy != null) {
openable = (Openable)workingCopy;
} else {
openable = this.handleFactory.createOpenable(pathString, this.scope);
if (openable == null)
continue; // match is outside classpath
}
// create new parser and lookup environment if this is a new project
IResource resource = null;
JavaProject javaProject = null;
try {
javaProject = (JavaProject) openable.getJavaProject();
if (workingCopy != null) {
resource = workingCopy.getOriginalElement().getResource();
} else {
resource = openable.getResource();
}
if (resource == null) { // case of a file in an external jar
resource = javaProject.getProject();
}
if (!javaProject.equals(previousJavaProject)) {
// locate matches in previous project
if (previousJavaProject != null) {
try {
this.locateMatches(previousJavaProject);
} catch (JavaModelException e) {
if (e.getException() instanceof CoreException) {
throw e;
} else {
// problem with classpath in this project -> skip it
}
}
this.matchingOpenables = new MatchingOpenableSet();
}
// initialization for this project
if (length == 1) {
// if only one potential match, a file name environment costs too much,
// so use the existing searchable environment wich will populate the java model
// only for this potential match and its required types.
if (this.nameEnvironment != null) { // cleanup
this.nameEnvironment.cleanup();
}
this.nameEnvironment = javaProject.getSearchableNameEnvironment();
} else {
this.initializeNameEnvironment(javaProject);
}
this.initialize(javaProject);
previousJavaProject = javaProject;
}
} catch (JavaModelException e) {
// file doesn't exist -> skip it
continue;
}
// add matching openable
this.addMatchingOpenable(resource, openable, null/*no CompilationUnitDeclaration yet*/, null/*no Matchset yet*/);
if (progressMonitor != null) {
progressMonitor.worked(1);
}
}
// last project
if (previousJavaProject != null) {
try {
this.locateMatches(previousJavaProject);
} catch (JavaModelException e) {
if (e.getException() instanceof CoreException) {
throw e;
} else {
// problem with classpath in last project -> skip it
}
}
this.matchingOpenables = new MatchingOpenableSet();
}
if (progressMonitor != null) {
progressMonitor.done();
}
} finally {
if (this.nameEnvironment != null) {
this.nameEnvironment.cleanup();
}
this.parsedUnits = null;
manager.flushZipFiles();
}
}
/**
* Locates the package declarations corresponding to this locator's pattern.
*/
public void locatePackageDeclarations(IWorkspace workspace)
throws JavaModelException {
this.locatePackageDeclarations(this.pattern, workspace);
}
/**
* Locates the package declarations corresponding to the search pattern.
*/
private void locatePackageDeclarations(
SearchPattern searchPattern,
IWorkspace workspace)
throws JavaModelException {
if (searchPattern instanceof OrPattern) {
OrPattern orPattern = (OrPattern) searchPattern;
this.locatePackageDeclarations(orPattern.leftPattern, workspace);
this.locatePackageDeclarations(orPattern.rightPattern, workspace);
} else
if (searchPattern instanceof PackageDeclarationPattern) {
PackageDeclarationPattern pkgPattern =
(PackageDeclarationPattern) searchPattern;
IJavaProject[] projects =
JavaModelManager.getJavaModelManager().getJavaModel().getJavaProjects();
for (int i = 0, length = projects.length; i < length; i++) {
IJavaProject javaProject = projects[i];
IPackageFragmentRoot[] roots = javaProject.getPackageFragmentRoots();
for (int j = 0, rootsLength = roots.length; j < rootsLength; j++) {
IJavaElement[] pkgs = roots[j].getChildren();
for (int k = 0, pksLength = pkgs.length; k < pksLength; k++) {
IPackageFragment pkg = (IPackageFragment)pkgs[k];
if (pkg.getChildren().length > 0
&& pkgPattern.matchesName(pkgPattern.pkgName, pkg.getElementName().toCharArray())) {
IResource resource = pkg.getResource();
if (resource == null) { // case of a file in an external jar
resource = javaProject.getProject();
}
this.currentMatchingOpenable = new MatchingOpenable(this, resource, null, null, null);
try {
this.report(-1, -2, pkg, IJavaSearchResultCollector.EXACT_MATCH);
} catch (CoreException e) {
if (e instanceof JavaModelException) {
throw (JavaModelException) e;
} else {
throw new JavaModelException(e);
}
}
}
}
}
}
}
}
public IType lookupType(TypeBinding typeBinding) {
char[] packageName = typeBinding.qualifiedPackageName();
char[] typeName = typeBinding.qualifiedSourceName();
// find package fragments
IPackageFragment[] pkgs =
this.nameLookup.findPackageFragments(
(packageName == null || packageName.length == 0) ?
IPackageFragment.DEFAULT_PACKAGE_NAME :
new String(packageName),
false);
// iterate type lookup in each package fragment
for (int i = 0, length = pkgs == null ? 0 : pkgs.length; i < length; i++) {
IType type =
this.nameLookup.findType(
new String(typeName),
pkgs[i],
false,
typeBinding.isClass() ?
NameLookup.ACCEPT_CLASSES:
NameLookup.ACCEPT_INTERFACES);
if (type != null) return type;
}
// search inside enclosing element
char[][] qualifiedName = CharOperation.splitOn('.', typeName);
int length = qualifiedName.length;
if (length == 0) return null;
IType type = this.createTypeHandle(qualifiedName[0]);
if (type == null) return null;
for (int i = 1; i < length; i++) {
type = this.createTypeHandle(type, qualifiedName[i]);
if (type == null) return null;
}
if (type.exists()) return type;
return null;
}
public void report(
int sourceStart,
int sourceEnd,
IJavaElement element,
int accuracy)
throws CoreException {
if (this.scope.encloses(element)) {
if (SearchEngine.VERBOSE) {
IResource res = this.getCurrentResource();
System.out.println("Reporting match"); //$NON-NLS-1$
System.out.println("\tResource: " + (res == null ? " <unknown> " : res.getFullPath().toString())); //$NON-NLS-2$//$NON-NLS-1$
System.out.println("\tPositions: [" + sourceStart + ", " + sourceEnd + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
System.out.println("\tJava element: " + ((JavaElement)element).toStringWithAncestors()); //$NON-NLS-1$
if (accuracy == IJavaSearchResultCollector.EXACT_MATCH) {
System.out.println("\tAccuracy: EXACT_MATCH"); //$NON-NLS-1$
} else {
System.out.println("\tAccuracy: POTENTIAL_MATCH"); //$NON-NLS-1$
}
}
this.report(
this.getCurrentResource(),
sourceStart,
sourceEnd,
element,
accuracy);
}
}
public void report(
IResource resource,
int sourceStart,
int sourceEnd,
IJavaElement element,
int accuracy)
throws CoreException {
this.collector.accept(
resource,
sourceStart,
sourceEnd + 1,
element,
accuracy);
}
public void reportBinaryMatch(
IMember binaryMember,
IBinaryType info,
int accuracy)
throws CoreException, JavaModelException {
this.reportBinaryMatch(null, binaryMember, info, accuracy);
}
public void reportBinaryMatch(
IResource resource,
IMember binaryMember,
IBinaryType info,
int accuracy)
throws CoreException, JavaModelException {
ISourceRange range = binaryMember.getNameRange();
if (range.getOffset() == -1) {
ClassFile classFile = (ClassFile) binaryMember.getClassFile();
SourceMapper mapper = classFile.getSourceMapper();
if (mapper != null) {
IType type = classFile.getType();
String sourceFileName = mapper.findSourceFileName(type, info);
if (sourceFileName != null) {
char[] contents = mapper.findSource(type, sourceFileName);
if (contents != null) {
range = mapper.mapSource(type, contents, binaryMember);
}
}
}
}
int startIndex = range.getOffset();
int endIndex = startIndex + range.getLength() - 1;
if (resource == null) {
this.report(startIndex, endIndex, binaryMember, accuracy);
} else {
this.report(resource, startIndex, endIndex, binaryMember, accuracy);
}
}
/**
* Reports the given field declaration to the search requestor.
*/
public void reportFieldDeclaration(
FieldDeclaration fieldDeclaration,
IJavaElement parent,
int accuracy)
throws CoreException {
// accept field declaration
this.report(
fieldDeclaration.sourceStart,
fieldDeclaration.sourceEnd,
(parent instanceof IType) ?
((IType)parent).getField(new String(fieldDeclaration.name)) :
parent,
accuracy);
}
/**
* Reports the given import to the search requestor.
*/
public void reportImport(ImportReference reference, int accuracy)
throws CoreException {
// create defining import handle
IJavaElement importHandle = this.createImportHandle(reference);
// accept reference
this.pattern.matchReportImportRef(reference, null, importHandle, accuracy, this);
}
/**
* Reports the given method declaration to the search requestor.
*/
public void reportMethodDeclaration(
AbstractMethodDeclaration methodDeclaration,
IJavaElement parent,
int accuracy)
throws CoreException {
IJavaElement enclosingElement;
if (parent instanceof IType) {
// create method handle
enclosingElement = this.createMethodHandle(methodDeclaration, (IType)parent);
if (enclosingElement == null) return;
} else {
enclosingElement = parent;
}
// compute source positions of the selector
Scanner scanner = parser.scanner;
int nameSourceStart = methodDeclaration.sourceStart;
scanner.setSource(
this.currentMatchingOpenable.getSource());
scanner.resetTo(nameSourceStart, methodDeclaration.sourceEnd);
try {
scanner.getNextToken();
} catch (InvalidInputException e) {
}
int nameSourceEnd = scanner.currentPosition - 1;
// accept method declaration
this.report(nameSourceStart, nameSourceEnd, enclosingElement, accuracy);
}
/**
* Reports the given package declaration to the search requestor.
*/
public void reportPackageDeclaration(ImportReference node) {
// TBD
}
/**
* Reports the given package reference to the search requestor.
*/
public void reportPackageReference(ImportReference node) {
// TBD
}
/**
* Finds the accurate positions of the sequence of tokens given by qualifiedName
* in the source and reports a reference to this this qualified name
* to the search requestor.
*/
public void reportAccurateReference(
int sourceStart,
int sourceEnd,
char[][] qualifiedName,
IJavaElement element,
int accuracy)
throws CoreException {
if (accuracy == -1) return;
// compute source positions of the qualified reference
Scanner scanner = parser.scanner;
scanner.setSource(
this.currentMatchingOpenable.getSource());
scanner.resetTo(sourceStart, sourceEnd);
int refSourceStart = -1, refSourceEnd = -1;
int tokenNumber = qualifiedName.length;
int token = -1;
int previousValid = -1;
int i = 0;
int currentPosition;
do {
// find first token that is an identifier (parenthesized expressions include parenthesises in source range - see bug 20693 - Finding references to variables does not find all occurrences )
do {
currentPosition = scanner.currentPosition;
try {
token = scanner.getNextToken();
} catch (InvalidInputException e) {
}
} while (token != TerminalTokens.TokenNameIdentifier && token != TerminalTokens.TokenNameEOF);
if (token != TerminalTokens.TokenNameEOF) {
char[] currentTokenSource = scanner.getCurrentTokenSource();
boolean equals = false;
while (i < tokenNumber
&& !(equals = this.pattern.matchesName(qualifiedName[i++], currentTokenSource))) {
}
if (equals && (previousValid == -1 || previousValid == i - 2)) {
previousValid = i - 1;
if (refSourceStart == -1) {
refSourceStart = currentPosition;
}
refSourceEnd = scanner.currentPosition - 1;
} else {
i = 0;
refSourceStart = -1;
previousValid = -1;
}
// read '.'
try {
token = scanner.getNextToken();
} catch (InvalidInputException e) {
}
}
if (i == tokenNumber) {
// accept reference
if (refSourceStart != -1) {
this.report(refSourceStart, refSourceEnd, element, accuracy);
} else {
this.report(sourceStart, sourceEnd, element, accuracy);
}
return;
}
} while (token != TerminalTokens.TokenNameEOF);
}
/**
* Finds the accurate positions of each valid token in the source and
* reports a reference to this token to the search requestor.
* A token is valid if it has an accurracy which is not -1.
*/
public void reportAccurateReference(
int sourceStart,
int sourceEnd,
char[][] tokens,
IJavaElement element,
int[] accuracies)
throws CoreException {
// compute source positions of the qualified reference
Scanner scanner = parser.scanner;
scanner.setSource(
this.currentMatchingOpenable.getSource());
scanner.resetTo(sourceStart, sourceEnd);
int refSourceStart = -1, refSourceEnd = -1;
int length = tokens.length;
int token = -1;
int previousValid = -1;
int i = 0;
int accuracyIndex = 0;
do {
int currentPosition = scanner.currentPosition;
// read token
try {
token = scanner.getNextToken();
} catch (InvalidInputException e) {
}
if (token != TerminalTokens.TokenNameEOF) {
char[] currentTokenSource = scanner.getCurrentTokenSource();
boolean equals = false;
while (i < length
&& !(equals = this.pattern.matchesName(tokens[i++], currentTokenSource))) {
}
if (equals && (previousValid == -1 || previousValid == i - 2)) {
previousValid = i - 1;
if (refSourceStart == -1) {
refSourceStart = currentPosition;
}
refSourceEnd = scanner.currentPosition - 1;
} else {
i = 0;
refSourceStart = -1;
previousValid = -1;
}
// read '.'
try {
token = scanner.getNextToken();
} catch (InvalidInputException e) {
}
}
if (accuracies[accuracyIndex] != -1) {
// accept reference
if (refSourceStart != -1) {
this.report(refSourceStart, refSourceEnd, element, accuracies[accuracyIndex]);
} else {
this.report(sourceStart, sourceEnd, element, accuracies[accuracyIndex]);
}
i = 0;
}
refSourceStart = -1;
previousValid = -1;
if (accuracyIndex < accuracies.length-1) {
accuracyIndex++;
}
} while (token != TerminalTokens.TokenNameEOF);
}
/**
* Reports the given reference to the search requestor.
* It is done in the given method and the method's defining types
* have the given simple names.
*/
public void reportReference(
AstNode reference,
AbstractMethodDeclaration methodDeclaration,
IJavaElement parent,
int accuracy)
throws CoreException {
IJavaElement enclosingElement;
if (parent instanceof IType) {
// create defining method handle
enclosingElement = this.createMethodHandle(methodDeclaration, (IType)parent);
if (enclosingElement == null) return; // case of a match found in a type other than the current class file
} else {
enclosingElement = parent;
}
// accept reference
this.pattern.matchReportReference(reference, enclosingElement, accuracy, this);
}
/**
* Reports the given reference to the search requestor.
* It is done in the given field and given type.
* The field's defining types have the given simple names.
*/
public void reportReference(
AstNode reference,
TypeDeclaration typeDeclaration,
FieldDeclaration fieldDeclaration,
IJavaElement parent,
int accuracy)
throws CoreException {
IJavaElement enclosingElement;
if (fieldDeclaration.isField()) {
if (parent instanceof IType) {
// create defining field handle
enclosingElement = this.createFieldHandle(fieldDeclaration, (IType)parent);
if (enclosingElement == null) return;
} else {
enclosingElement = parent;
}
// accept reference
this.pattern.matchReportReference(reference, enclosingElement, accuracy, this);
} else { // initializer
if (parent instanceof IType) {
// create defining initializer
enclosingElement =
this.createInitializerHandle(
typeDeclaration,
fieldDeclaration,
(IType)parent);
if (enclosingElement == null) return;
} else {
enclosingElement = parent;
}
// accept reference
this.pattern.matchReportReference(reference, enclosingElement, accuracy, this);
}
}
/**
* Reports the given super type reference to the search requestor.
* It is done in the given defining type (with the given simple names).
*/
public void reportSuperTypeReference(
TypeReference typeRef,
IJavaElement type,
int accuracy)
throws CoreException {
// accept type reference
this.pattern.matchReportReference(typeRef, type, accuracy, this);
}
/**
* Reports the given type declaration to the search requestor.
*/
public void reportTypeDeclaration(
TypeDeclaration typeDeclaration,
IJavaElement parent,
int accuracy)
throws CoreException {
// accept class or interface declaration
this.report(
typeDeclaration.sourceStart,
typeDeclaration.sourceEnd,
(parent == null) ?
this.createTypeHandle(typeDeclaration.name) :
(parent instanceof IType) ?
this.createTypeHandle((IType)parent, typeDeclaration.name) :
parent,
accuracy);
}
void addMatchingOpenable(
IResource resource,
Openable openable,
CompilationUnitDeclaration parsedUnit,
MatchSet matchSet) {
MatchingOpenable matchingOpenable = new MatchingOpenable(this, resource, openable, parsedUnit, matchSet);
if (matchingOpenable != null) {
this.matchingOpenables.add(matchingOpenable);
}
}
/*
* Caches the given binary type in the lookup environment and returns it.
* Returns the existing one if already cached.
* Returns null if source type binding was cached.
*/
BinaryTypeBinding cacheBinaryType(IType type) throws JavaModelException {
IType enclosingType = type.getDeclaringType();
if (enclosingType != null) {
// force caching of enclosing types first, so that binary type can be found in lookup enviroment
this.cacheBinaryType(enclosingType);
}
IBinaryType binaryType = (IBinaryType)((BinaryType)type).getElementInfo();
BinaryTypeBinding binding = this.lookupEnvironment.cacheBinaryType(binaryType);
if (binding == null) { // it was already cached as a result of a previous query
char[][] compoundName = CharOperation.splitOn('.', type.getFullyQualifiedName().toCharArray());
ReferenceBinding referenceBinding = this.lookupEnvironment.getCachedType(compoundName);
if (referenceBinding != null && (referenceBinding instanceof BinaryTypeBinding)) {
// if the binding could be found and if it comes from a binary type,
binding = (BinaryTypeBinding)referenceBinding;
}
}
return binding;
}
private INameEnvironment getNameEnvironment(JavaProject project) throws JavaModelException {
//return project.getSearchableNameEnvironment();
return new JavaSearchNameEnvironment(project);
}
public CompilationUnitDeclaration dietParse(final char[] source) {
// source unit
ICompilationUnit sourceUnit = new ICompilationUnit() {
public char[] getContents() {
return source;
}
public char[] getFileName() {
return EMPTY_FILE_NAME; // not used
}
public char[] getMainTypeName() {
return null; // don't need to check if main type name == compilation unit name
}
public char[][] getPackageName() {
return null;
}
};
// diet parse
CompilationResult compilationResult = new CompilationResult(sourceUnit, 0, 0, 0);
return this.parser.dietParse(sourceUnit, compilationResult);
}
public char[] findSource(ClassFile classFile) {
char[] source = null;
try {
SourceMapper sourceMapper = classFile.getSourceMapper();
if (sourceMapper != null) {
IType type = classFile.getType();
if (classFile.isOpen() && type.getDeclaringType() == null) {
source = sourceMapper.findSource(type);
} else {
ClassFileReader reader = this.classFileReader(type);
if (reader != null) {
String sourceFileName = sourceMapper.findSourceFileName(type, reader);
if (sourceFileName != null) {
source = sourceMapper.findSource(type, sourceFileName);
}
}
}
}
} catch (JavaModelException e) {
}
return source;
}
public IBinaryType getBinaryInfo(org.eclipse.jdt.internal.core.ClassFile classFile, IResource resource) throws CoreException {
BinaryType binaryType = (BinaryType)classFile.getType();
if (classFile.isOpen()) {
// reuse the info from the java model cache
return (IBinaryType)binaryType.getElementInfo();
} else {
// create a temporary info
IBinaryType info;
try {
IJavaElement pkg = classFile.getParent();
PackageFragmentRoot root = (PackageFragmentRoot)pkg.getParent();
if (root.isArchive()) {
// class file in a jar
String pkgPath = pkg.getElementName().replace('.', '/');
String classFilePath =
(pkgPath.length() > 0) ?
pkgPath + "/" + classFile.getElementName() : //$NON-NLS-1$
classFile.getElementName();
ZipFile zipFile = null;
try {
zipFile = ((JarPackageFragmentRoot)root).getJar();
info = org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader.read(
zipFile,
classFilePath);
} finally {
JavaModelManager.getJavaModelManager().closeZipFile(zipFile);
}
} else {
// class file in a directory
String osPath = resource.getFullPath().toOSString();
info = org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader.read(osPath);
}
return info;
} catch (org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException e) {
//e.printStackTrace();
return null;
} catch (java.io.IOException e) {
throw new JavaModelException(e, IJavaModelStatusConstants.IO_EXCEPTION);
}
}
}
protected Openable getCurrentOpenable() {
return this.currentMatchingOpenable.openable;
}
/**
* Locate the matches amongst the matching openables.
*/
private void locateMatches(JavaProject javaProject) throws JavaModelException {
MatchingOpenable[] openables = this.matchingOpenables.getMatchingOpenables(javaProject.getPackageFragmentRoots());
this.compilationAborted = false;
if (this.pattern.needsResolve) {
this.createAndResolveBindings(openables);
}
// create hierarchy resolver if scope is a hierarchy scope
if (this.scope instanceof HierarchyScope) {
IType focusType = ((HierarchyScope)this.scope).focusType;
if (focusType != null) {
if (!focusType.isBinary()) {
// cache all types in the focus' compilation unit (even secondary types)
this.accept((ICompilationUnit)focusType.getCompilationUnit());
}
char[] fullyQualifiedName = focusType.getFullyQualifiedName().toCharArray();
this.hierarchyResolver = new HierarchyResolver(this.lookupEnvironment, null/*hierarchy is not going to be computed*/);
if (this.hierarchyResolver.setFocusType(CharOperation.splitOn('.', fullyQualifiedName)) == null) {
// focus type is not visible from this project
return;
}
} else {
this.hierarchyResolver = null;
}
} else {
this.hierarchyResolver = null;
}
// matching openable resolution
for (int i = 0, length = openables.length; i < length; i++) {
if (progressMonitor != null && progressMonitor.isCanceled()) {
throw new OperationCanceledException();
}
try {
this.currentMatchingOpenable = openables[i];
if (!this.currentMatchingOpenable.hasAlreadyDefinedType()) {
this.currentMatchingOpenable.locateMatches();
} // else skip type has it is hidden so not visible
} catch (AbortCompilation e) {
// problem with class path: it could not find base classes
// continue and try next matching openable reporting innacurate matches (since bindings will be null)
compilationAborted = true;
} catch (CoreException e) {
if (e instanceof JavaModelException) {
// problem with class path: it could not find base classes
// continue and try next matching openable reporting innacurate matches (since bindings will be null)
compilationAborted = true;
} else {
// core exception thrown by client's code: let it through
throw new JavaModelException(e);
}
} finally {
this.currentMatchingOpenable.reset();
}
if (progressMonitor != null) {
progressMonitor.worked(3);
}
}
this.currentMatchingOpenable = null;
}
void createAndResolveBindings(MatchingOpenable[] openables) {
// binding creation
for (int i = 0, length = openables.length; i < length; i++) {
openables[i].buildTypeBindings();
if (this.progressMonitor != null) {
if (this.progressMonitor.isCanceled()) {
throw new OperationCanceledException();
} else {
this.progressMonitor.worked(6);
}
}
}
// binding resolution
try {
this.lookupEnvironment.completeTypeBindings();
} catch (AbortCompilation e) {
// problem with class path: it could not find base classes
// continue reporting innacurate matches (since bindings will be null)
this.compilationAborted = true;
}
}
}