/*******************************************************************************
* 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;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.WorkingCopyOwner;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.internal.codeassist.ISearchRequestor;
import org.eclipse.jdt.internal.compiler.env.AccessRestriction;
import org.eclipse.jdt.internal.compiler.env.IBinaryType;
import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
import org.eclipse.jdt.internal.compiler.env.ISourceType;
import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
import org.eclipse.jdt.internal.core.search.BasicSearchEngine;
import org.eclipse.jdt.internal.core.search.IRestrictedAccessConstructorRequestor;
import org.eclipse.jdt.internal.core.search.IRestrictedAccessTypeRequestor;
import org.eclipse.jdt.internal.core.search.indexing.IndexManager;
import org.eclipse.jdt.internal.core.util.Util;
/**
* This class provides a <code>SearchableBuilderEnvironment</code> for code assist which uses the
* Java model as a search tool.
*/
public class SearchableEnvironment
implements INameEnvironment, IJavaSearchConstants {
public NameLookup nameLookup;
protected ICompilationUnit unitToSkip;
protected org.eclipse.jdt.core.ICompilationUnit[] workingCopies;
protected WorkingCopyOwner owner;
protected JavaProject project;
protected IJavaSearchScope searchScope;
protected boolean checkAccessRestrictions;
/**
* Creates a SearchableEnvironment on the given project
*/
public SearchableEnvironment(JavaProject project, org.eclipse.jdt.core.ICompilationUnit[] workingCopies) throws JavaModelException {
this.project= project;
this.checkAccessRestrictions=
!JavaCore.IGNORE.equals(project.getOption(JavaCore.COMPILER_PB_FORBIDDEN_REFERENCE, true))
|| !JavaCore.IGNORE.equals(project.getOption(JavaCore.COMPILER_PB_DISCOURAGED_REFERENCE, true));
this.workingCopies= workingCopies;
this.nameLookup= project.newNameLookup(workingCopies);
}
/**
* Creates a SearchableEnvironment on the given project
*/
public SearchableEnvironment(JavaProject project, WorkingCopyOwner owner) throws JavaModelException {
this(project, owner == null ? null : JavaModelManager.getJavaModelManager().getWorkingCopies(owner, true/*add primary WCs*/));
this.owner= owner;
}
private static int convertSearchFilterToModelFilter(int searchFilter) {
switch (searchFilter) {
case IJavaSearchConstants.CLASS:
return NameLookup.ACCEPT_CLASSES;
case IJavaSearchConstants.INTERFACE:
return NameLookup.ACCEPT_INTERFACES;
case IJavaSearchConstants.ENUM:
return NameLookup.ACCEPT_ENUMS;
case IJavaSearchConstants.ANNOTATION_TYPE:
return NameLookup.ACCEPT_ANNOTATIONS;
case IJavaSearchConstants.CLASS_AND_ENUM:
return NameLookup.ACCEPT_CLASSES | NameLookup.ACCEPT_ENUMS;
case IJavaSearchConstants.CLASS_AND_INTERFACE:
return NameLookup.ACCEPT_CLASSES | NameLookup.ACCEPT_INTERFACES;
default:
return NameLookup.ACCEPT_ALL;
}
}
/**
* Returns the given type in the the given package if it exists, otherwise <code>null</code>.
*/
protected NameEnvironmentAnswer find(String typeName, String packageName) {
if (packageName == null)
packageName= IPackageFragment.DEFAULT_PACKAGE_NAME;
if (this.owner != null) {
String source= this.owner.findSource(typeName, packageName);
if (source != null) {
ICompilationUnit cu= new BasicCompilationUnit(source.toCharArray(), CharOperation.splitOn('.', packageName.toCharArray()), typeName + Util.defaultJavaExtension());
return new NameEnvironmentAnswer(cu, null);
}
}
NameLookup.Answer answer=
this.nameLookup.findType(
typeName,
packageName,
false/*exact match*/,
NameLookup.ACCEPT_ALL,
this.checkAccessRestrictions);
if (answer != null) {
// construct name env answer
if (answer.type instanceof BinaryType) { // BinaryType
try {
return new NameEnvironmentAnswer((IBinaryType)((BinaryType)answer.type).getElementInfo(), answer.restriction);
} catch (JavaModelException npe) {
// fall back to using owner
}
} else { //SourceType
try {
// retrieve the requested type
SourceTypeElementInfo sourceType= (SourceTypeElementInfo)((SourceType)answer.type).getElementInfo();
ISourceType topLevelType= sourceType;
while (topLevelType.getEnclosingType() != null) {
topLevelType= topLevelType.getEnclosingType();
}
// find all siblings (other types declared in same unit, since may be used for name resolution)
IType[] types= sourceType.getHandle().getCompilationUnit().getTypes();
ISourceType[] sourceTypes= new ISourceType[types.length];
// in the resulting collection, ensure the requested type is the first one
sourceTypes[0]= sourceType;
int length= types.length;
for (int i= 0, index= 1; i < length; i++) {
ISourceType otherType=
(ISourceType)((JavaElement)types[i]).getElementInfo();
if (!otherType.equals(topLevelType) && index < length) // check that the index is in bounds (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=62861)
sourceTypes[index++]= otherType;
}
return new NameEnvironmentAnswer(sourceTypes, answer.restriction);
} catch (JavaModelException npe) {
// fall back to using owner
}
}
}
return null;
}
/**
* Find the packages that start with the given prefix. A valid prefix is a qualified name
* separated by periods (ex. java.util). The packages found are passed to:
* ISearchRequestor.acceptPackage(char[][] packageName)
*/
public void findPackages(char[] prefix, ISearchRequestor requestor) {
this.nameLookup.seekPackageFragments(
new String(prefix),
true,
new SearchableEnvironmentRequestor(requestor));
}
/**
* Find the top-level types that are defined in the current environment and whose simple name
* matches the given name.
*
* The types found are passed to one of the following methods (if additional information is
* known about the types): ISearchRequestor.acceptType(char[][] packageName, char[] typeName)
* ISearchRequestor.acceptClass(char[][] packageName, char[] typeName, int modifiers)
* ISearchRequestor.acceptInterface(char[][] packageName, char[] typeName, int modifiers)
*
* This method can not be used to find member types... member types are found relative to their
* enclosing type.
*/
public void findExactTypes(char[] name, final boolean findMembers, int searchFor, final ISearchRequestor storage) {
try {
final String excludePath;
if (this.unitToSkip != null) {
if (!(this.unitToSkip instanceof IJavaElement)) {
// revert to model investigation
findExactTypes(
new String(name),
storage,
convertSearchFilterToModelFilter(searchFor));
return;
}
excludePath= ((IJavaElement)this.unitToSkip).getPath().toString();
} else {
excludePath= null;
}
IProgressMonitor progressMonitor= new IProgressMonitor() {
boolean isCanceled= false;
public void beginTask(String n, int totalWork) {
// implements interface method
}
public void done() {
// implements interface method
}
public void internalWorked(double work) {
// implements interface method
}
public boolean isCanceled() {
return this.isCanceled;
}
public void setCanceled(boolean value) {
this.isCanceled= value;
}
public void setTaskName(String n) {
// implements interface method
}
public void subTask(String n) {
// implements interface method
}
public void worked(int work) {
// implements interface method
}
};
IRestrictedAccessTypeRequestor typeRequestor= new IRestrictedAccessTypeRequestor() {
public void acceptType(int modifiers, char[] packageName, char[] simpleTypeName, char[][] enclosingTypeNames, String path, AccessRestriction access) {
if (excludePath != null && excludePath.equals(path))
return;
if (!findMembers && enclosingTypeNames != null && enclosingTypeNames.length > 0)
return; // accept only top level types
storage.acceptType(packageName, simpleTypeName, enclosingTypeNames, modifiers, access);
}
};
try {
new BasicSearchEngine(this.workingCopies).searchAllTypeNames(
null,
SearchPattern.R_EXACT_MATCH,
name,
SearchPattern.R_EXACT_MATCH,
searchFor,
getSearchScope(),
typeRequestor,
CANCEL_IF_NOT_READY_TO_SEARCH,
progressMonitor);
} catch (OperationCanceledException e) {
findExactTypes(
new String(name),
storage,
convertSearchFilterToModelFilter(searchFor));
}
} catch (JavaModelException e) {
findExactTypes(
new String(name),
storage,
convertSearchFilterToModelFilter(searchFor));
}
}
/**
* Returns all types whose simple name matches with the given <code>name</code>.
*/
private void findExactTypes(String name, ISearchRequestor storage, int type) {
SearchableEnvironmentRequestor requestor=
new SearchableEnvironmentRequestor(storage, this.unitToSkip, this.project, this.nameLookup);
this.nameLookup.seekTypes(name, null, false, type, requestor);
}
/**
* @see org.eclipse.jdt.internal.compiler.env.INameEnvironment#findType(char[][])
*/
public NameEnvironmentAnswer findType(char[][] compoundTypeName) {
if (compoundTypeName == null)
return null;
int length= compoundTypeName.length;
if (length <= 1) {
if (length == 0)
return null;
return find(new String(compoundTypeName[0]), null);
}
int lengthM1= length - 1;
char[][] packageName= new char[lengthM1][];
System.arraycopy(compoundTypeName, 0, packageName, 0, lengthM1);
return find(
new String(compoundTypeName[lengthM1]),
CharOperation.toString(packageName));
}
/**
* @see org.eclipse.jdt.internal.compiler.env.INameEnvironment#findType(char[], char[][])
*/
public NameEnvironmentAnswer findType(char[] name, char[][] packageName) {
if (name == null)
return null;
return find(
new String(name),
packageName == null || packageName.length == 0 ? null : CharOperation.toString(packageName));
}
/**
* Find the top-level types that are defined in the current environment and whose name starts
* with the given prefix. The prefix is a qualified name separated by periods or a simple name
* (ex. java.util.V or V).
*
* The types found are passed to one of the following methods (if additional information is
* known about the types): ISearchRequestor.acceptType(char[][] packageName, char[] typeName)
* ISearchRequestor.acceptClass(char[][] packageName, char[] typeName, int modifiers)
* ISearchRequestor.acceptInterface(char[][] packageName, char[] typeName, int modifiers)
*
* This method can not be used to find member types... member types are found relative to their
* enclosing type.
*/
public void findTypes(char[] prefix, final boolean findMembers, boolean camelCaseMatch, int searchFor, final ISearchRequestor storage) {
findTypes(prefix, findMembers, camelCaseMatch, searchFor, storage, null);
}
/**
* Must be used only by CompletionEngine. The progress monitor is used to be able to cancel
* completion operations
*
* Find the top-level types that are defined in the current environment and whose name starts
* with the given prefix. The prefix is a qualified name separated by periods or a simple name
* (ex. java.util.V or V).
*
* The types found are passed to one of the following methods (if additional information is
* known about the types): ISearchRequestor.acceptType(char[][] packageName, char[] typeName)
* ISearchRequestor.acceptClass(char[][] packageName, char[] typeName, int modifiers)
* ISearchRequestor.acceptInterface(char[][] packageName, char[] typeName, int modifiers)
*
* This method can not be used to find member types... member types are found relative to their
* enclosing type.
*/
public void findTypes(char[] prefix, final boolean findMembers, boolean camelCaseMatch, int searchFor, final ISearchRequestor storage, IProgressMonitor monitor) {
/*
if (true){
findTypes(new String(prefix), storage, NameLookup.ACCEPT_CLASSES | NameLookup.ACCEPT_INTERFACES);
return;
}
*/
try {
final String excludePath;
if (this.unitToSkip != null) {
if (!(this.unitToSkip instanceof IJavaElement)) {
// revert to model investigation
findTypes(
new String(prefix),
storage,
convertSearchFilterToModelFilter(searchFor));
return;
}
excludePath= ((IJavaElement)this.unitToSkip).getPath().toString();
} else {
excludePath= null;
}
int lastDotIndex= CharOperation.lastIndexOf('.', prefix);
char[] qualification, simpleName;
if (lastDotIndex < 0) {
qualification= null;
if (camelCaseMatch) {
simpleName= prefix;
} else {
simpleName= CharOperation.toLowerCase(prefix);
}
} else {
qualification= CharOperation.subarray(prefix, 0, lastDotIndex);
if (camelCaseMatch) {
simpleName= CharOperation.subarray(prefix, lastDotIndex + 1, prefix.length);
} else {
simpleName=
CharOperation.toLowerCase(
CharOperation.subarray(prefix, lastDotIndex + 1, prefix.length));
}
}
IProgressMonitor progressMonitor= new IProgressMonitor() {
boolean isCanceled= false;
public void beginTask(String name, int totalWork) {
// implements interface method
}
public void done() {
// implements interface method
}
public void internalWorked(double work) {
// implements interface method
}
public boolean isCanceled() {
return this.isCanceled;
}
public void setCanceled(boolean value) {
this.isCanceled= value;
}
public void setTaskName(String name) {
// implements interface method
}
public void subTask(String name) {
// implements interface method
}
public void worked(int work) {
// implements interface method
}
};
IRestrictedAccessTypeRequestor typeRequestor= new IRestrictedAccessTypeRequestor() {
public void acceptType(int modifiers, char[] packageName, char[] simpleTypeName, char[][] enclosingTypeNames, String path, AccessRestriction access) {
if (excludePath != null && excludePath.equals(path))
return;
if (!findMembers && enclosingTypeNames != null && enclosingTypeNames.length > 0)
return; // accept only top level types
storage.acceptType(packageName, simpleTypeName, enclosingTypeNames, modifiers, access);
}
};
int matchRule= SearchPattern.R_PREFIX_MATCH;
if (camelCaseMatch)
matchRule|= SearchPattern.R_CAMELCASE_MATCH;
if (monitor != null) {
IndexManager indexManager= JavaModelManager.getIndexManager();
if (indexManager.awaitingJobsCount() == 0) {
// indexes were already there, so perform an immediate search to avoid any index rebuilt
new BasicSearchEngine(this.workingCopies).searchAllTypeNames(
qualification,
SearchPattern.R_EXACT_MATCH,
simpleName,
matchRule, // not case sensitive
searchFor,
getSearchScope(),
typeRequestor,
FORCE_IMMEDIATE_SEARCH,
progressMonitor);
} else {
// indexes were not ready, give the indexing a chance to finish small jobs by sleeping 100ms...
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// Do nothing
}
if (monitor.isCanceled()) {
throw new OperationCanceledException();
}
if (indexManager.awaitingJobsCount() == 0) {
// indexes are now ready, so perform an immediate search to avoid any index rebuilt
new BasicSearchEngine(this.workingCopies).searchAllTypeNames(
qualification,
SearchPattern.R_EXACT_MATCH,
simpleName,
matchRule, // not case sensitive
searchFor,
getSearchScope(),
typeRequestor,
FORCE_IMMEDIATE_SEARCH,
progressMonitor);
} else {
// Indexes are still not ready, so look for types in the model instead of a search request
findTypes(
new String(prefix),
storage,
convertSearchFilterToModelFilter(searchFor));
}
}
} else {
try {
new BasicSearchEngine(this.workingCopies).searchAllTypeNames(
qualification,
SearchPattern.R_EXACT_MATCH,
simpleName,
matchRule, // not case sensitive
searchFor,
getSearchScope(),
typeRequestor,
CANCEL_IF_NOT_READY_TO_SEARCH,
progressMonitor);
} catch (OperationCanceledException e) {
findTypes(
new String(prefix),
storage,
convertSearchFilterToModelFilter(searchFor));
}
}
} catch (JavaModelException e) {
findTypes(
new String(prefix),
storage,
convertSearchFilterToModelFilter(searchFor));
}
}
/**
* Must be used only by CompletionEngine. The progress monitor is used to be able to cancel
* completion operations
*
* Find constructor declarations that are defined in the current environment and whose name
* starts with the given prefix. The prefix is a qualified name separated by periods or a simple
* name (ex. java.util.V or V).
*
* The constructors found are passed to one of the following methods:
* ISearchRequestor.acceptConstructor(...)
*/
public void findConstructorDeclarations(char[] prefix, boolean camelCaseMatch, final ISearchRequestor storage, IProgressMonitor monitor) {
try {
final String excludePath;
if (this.unitToSkip != null && this.unitToSkip instanceof IJavaElement) {
excludePath= ((IJavaElement)this.unitToSkip).getPath().toString();
} else {
excludePath= null;
}
int lastDotIndex= CharOperation.lastIndexOf('.', prefix);
char[] qualification, simpleName;
if (lastDotIndex < 0) {
qualification= null;
if (camelCaseMatch) {
simpleName= prefix;
} else {
simpleName= CharOperation.toLowerCase(prefix);
}
} else {
qualification= CharOperation.subarray(prefix, 0, lastDotIndex);
if (camelCaseMatch) {
simpleName= CharOperation.subarray(prefix, lastDotIndex + 1, prefix.length);
} else {
simpleName=
CharOperation.toLowerCase(
CharOperation.subarray(prefix, lastDotIndex + 1, prefix.length));
}
}
IProgressMonitor progressMonitor= new IProgressMonitor() {
boolean isCanceled= false;
public void beginTask(String name, int totalWork) {
// implements interface method
}
public void done() {
// implements interface method
}
public void internalWorked(double work) {
// implements interface method
}
public boolean isCanceled() {
return this.isCanceled;
}
public void setCanceled(boolean value) {
this.isCanceled= value;
}
public void setTaskName(String name) {
// implements interface method
}
public void subTask(String name) {
// implements interface method
}
public void worked(int work) {
// implements interface method
}
};
IRestrictedAccessConstructorRequestor constructorRequestor= new IRestrictedAccessConstructorRequestor() {
public void acceptConstructor(
int modifiers,
char[] simpleTypeName,
int parameterCount,
char[] signature,
char[][] parameterTypes,
char[][] parameterNames,
int typeModifiers,
char[] packageName,
int extraFlags,
String path,
AccessRestriction access) {
if (excludePath != null && excludePath.equals(path))
return;
storage.acceptConstructor(
modifiers,
simpleTypeName,
parameterCount,
signature,
parameterTypes,
parameterNames,
typeModifiers,
packageName,
extraFlags,
path,
access);
}
};
int matchRule= SearchPattern.R_PREFIX_MATCH;
if (camelCaseMatch)
matchRule|= SearchPattern.R_CAMELCASE_MATCH;
if (monitor != null) {
IndexManager indexManager= JavaModelManager.getIndexManager();
while (indexManager.awaitingJobsCount() > 0) {
try {
Thread.sleep(50); // indexes are not ready, sleep 50ms...
} catch (InterruptedException e) {
// Do nothing
}
if (monitor.isCanceled()) {
throw new OperationCanceledException();
}
}
new BasicSearchEngine(this.workingCopies).searchAllConstructorDeclarations(
qualification,
simpleName,
matchRule,
getSearchScope(),
constructorRequestor,
FORCE_IMMEDIATE_SEARCH,
progressMonitor);
} else {
try {
new BasicSearchEngine(this.workingCopies).searchAllConstructorDeclarations(
qualification,
simpleName,
matchRule,
getSearchScope(),
constructorRequestor,
CANCEL_IF_NOT_READY_TO_SEARCH,
progressMonitor);
} catch (OperationCanceledException e) {
// Do nothing
}
}
} catch (JavaModelException e) {
// Do nothing
}
}
/**
* Returns all types whose name starts with the given (qualified) <code>prefix</code>.
*
* If the <code>prefix</code> is unqualified, all types whose simple name matches the
* <code>prefix</code> are returned.
*/
private void findTypes(String prefix, ISearchRequestor storage, int type) {
//TODO (david) should add camel case support
SearchableEnvironmentRequestor requestor=
new SearchableEnvironmentRequestor(storage, this.unitToSkip, this.project, this.nameLookup);
int index= prefix.lastIndexOf('.');
if (index == -1) {
this.nameLookup.seekTypes(prefix, null, true, type, requestor);
} else {
String packageName= prefix.substring(0, index);
JavaElementRequestor elementRequestor= new JavaElementRequestor();
this.nameLookup.seekPackageFragments(packageName, false, elementRequestor);
IPackageFragment[] fragments= elementRequestor.getPackageFragments();
if (fragments != null) {
String className= prefix.substring(index + 1);
for (int i= 0, length= fragments.length; i < length; i++)
if (fragments[i] != null)
this.nameLookup.seekTypes(className, fragments[i], true, type, requestor);
}
}
}
private IJavaSearchScope getSearchScope() {
if (this.searchScope == null) {
// Create search scope with visible entry on the project's classpath
if (this.checkAccessRestrictions) {
this.searchScope= BasicSearchEngine.createJavaSearchScope(new IJavaElement[] { this.project });
} else {
this.searchScope= BasicSearchEngine.createJavaSearchScope(this.nameLookup.packageFragmentRoots);
}
}
return this.searchScope;
}
/**
* @see org.eclipse.jdt.internal.compiler.env.INameEnvironment#isPackage(char[][], char[])
*/
public boolean isPackage(char[][] parentPackageName, char[] subPackageName) {
String[] pkgName;
if (parentPackageName == null)
pkgName= new String[] { new String(subPackageName) };
else {
int length= parentPackageName.length;
pkgName= new String[length + 1];
for (int i= 0; i < length; i++)
pkgName[i]= new String(parentPackageName[i]);
pkgName[length]= new String(subPackageName);
}
return (this.owner != null && this.owner.isPackage(pkgName))
|| this.nameLookup.isPackage(pkgName);
}
/**
* Returns a printable string for the array.
*/
protected String toStringChar(char[] name) {
return "[" //$NON-NLS-1$
+ new String(name) + "]"; //$NON-NLS-1$
}
/**
* Returns a printable string for the array.
*/
protected String toStringCharChar(char[][] names) {
StringBuffer result= new StringBuffer();
for (int i= 0; i < names.length; i++) {
result.append(toStringChar(names[i]));
}
return result.toString();
}
public void cleanup() {
// nothing to do
}
}