blob: 8ebdbe1ca6e2eae909eb862c496de4580fbfdb9c [file] [log] [blame]
package org.eclipse.jdt.internal.core;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import org.eclipse.core.runtime.*;
import org.eclipse.core.resources.*;
import java.util.Vector;
import java.util.Hashtable;
import java.io.File;
import org.eclipse.jdt.core.*;
/**
* This class implements basic namelookup functionality as
* described in <code>INameLookup</code>. It performs its
* searches by querying the Java Model directly.
*
* @see INameLookup
*/
public class NameLookup implements INameLookup {
/**
* The <code>IPackageFragmentRoot</code>'s associated
* with the classpath of this NameLookup facility's
* project.
*/
protected IPackageFragmentRoot[] fPackageFragmentRoots= null;
/**
* Table that maps package names to lists of package fragments for
* all package fragments in the package fragment roots known
* by this name lookup facility. To allow > 1 package fragment
* with the same name, values are arrays of package fragments
* ordered as they appear on the classpath.
*/
protected Hashtable fPackageFragments;
/**
* Singleton <code>SingleTypeRequestor</code>.
* @see findType(String, IPackageFragment, boolean, int)
*/
protected SingleTypeRequestor fgSingleTypeRequestor= new SingleTypeRequestor();
/**
* Singleton <code>JavaElementRequestor</code>.
* @see findType(String, boolean, int)
*/
protected JavaElementRequestor fgJavaElementRequestor= new JavaElementRequestor();
/**
* The <code>IWorkspace</code> that this NameLookup
* is configure within.
*/
protected IWorkspace workspace;
public NameLookup(IJavaProject project) throws JavaModelException {
configureFromProject(project);
}
/**
* Returns true if:<ul>
* <li>the given type is an existing class and the flag's <code>ACCEPT_CLASSES</code>
* bit is on
* <li>the given type is an existing interface and the <code>ACCEPT_INTERFACES</code>
* bit is on
* <li>neither the <code>ACCEPT_CLASSES</code> or <code>ACCEPT_INTERFACES</code>
* bit is on
* </ul>
* Otherwise, false is returned.
*/
protected boolean acceptType(IType type, int acceptFlags) {
if (acceptFlags == 0)
return true; // no flags, always accepted
try {
if (type.isClass()) {
return (acceptFlags & ACCEPT_CLASSES) != 0;
} else {
return (acceptFlags & ACCEPT_INTERFACES) != 0;
}
} catch (JavaModelException npe) {
return false; // the class is not present, do not accept.
}
}
/**
* Configures this <code>NameLookup</code> based on the
* info of the given <code>IJavaProject</code>.
*
* @throws JavaModelException if the <code>IJavaProject</code> has no classpath.
*/
private void configureFromProject(IJavaProject project) throws JavaModelException {
workspace= project.getJavaModel().getWorkspace();
fPackageFragmentRoots= ((JavaProject) project).getAllPackageFragmentRoots();
fPackageFragments= new Hashtable();
IPackageFragment[] frags= ((JavaProject) project).getAllPackageFragments();
for (int i= 0; i < frags.length; i++) {
IPackageFragment fragment= frags[i];
IPackageFragment[] entry= (IPackageFragment[]) fPackageFragments.get(fragment.getElementName());
if (entry == null) {
entry= new IPackageFragment[1];
entry[0]= fragment;
fPackageFragments.put(fragment.getElementName(), entry);
} else {
IPackageFragment[] copy= new IPackageFragment[entry.length + 1];
System.arraycopy(entry, 0, copy, 0, entry.length);
copy[entry.length]= fragment;
fPackageFragments.put(fragment.getElementName(), copy);
}
}
}
/**
* Finds every type in the project whose simple name matches
* the prefix, informing the requestor of each hit. The requestor
* is polled for cancellation at regular intervals.
*
* <p>The <code>partialMatch</code> argument indicates partial matches
* should be considered.
*/
private void findAllTypes(String prefix, boolean partialMatch, int acceptFlags, IJavaElementRequestor requestor) {
int count= fPackageFragmentRoots.length;
for (int i= 0; i < count; i++) {
if (requestor.isCanceled())
return;
IPackageFragmentRoot root= fPackageFragmentRoots[i];
IJavaElement[] packages= null;
try {
packages= root.getChildren();
} catch (JavaModelException npe) {
continue; // the root is not present, continue;
}
if (packages != null) {
for (int j= 0, packageCount= packages.length; j < packageCount; j++) {
if (requestor.isCanceled())
return;
seekTypes(prefix, (IPackageFragment) packages[j], partialMatch, acceptFlags, requestor);
}
}
}
}
/**
* @see INameLookup
*/
public ICompilationUnit findCompilationUnit(String qualifiedTypeName) {
String pkgName= IPackageFragment.DEFAULT_PACKAGE_NAME;
String cuName= qualifiedTypeName;
int index= qualifiedTypeName.lastIndexOf('.');
if (index != -1) {
pkgName= qualifiedTypeName.substring(0, index);
cuName= qualifiedTypeName.substring(index + 1);
}
index= cuName.indexOf('$');
if (index != -1) {
cuName= cuName.substring(0, index);
}
cuName += ".java"/*nonNLS*/;
IPackageFragment[] frags= (IPackageFragment[]) fPackageFragments.get(pkgName);
if (frags != null) {
for (int i= 0; i < frags.length; i++) {
IPackageFragment frag= frags[i];
if (!(frag instanceof JarPackageFragment)) {
ICompilationUnit cu= frag.getCompilationUnit(cuName);
if (cu != null && cu.exists()) {
return cu;
}
}
}
}
return null;
}
/**
* @see INameLookup
*/
public IPackageFragment findPackageFragment(IPath path) {
if (!path.isAbsolute()) {
throw new IllegalArgumentException(Util.bind("path.mustBeAbsolute"/*nonNLS*/));
}
IResource possibleFragment = workspace.getRoot().findMember(path);
if (possibleFragment == null) {
//external jar
for (int i = 0; i < fPackageFragmentRoots.length; i++) {
IPackageFragmentRoot root = fPackageFragmentRoots[i];
if (!root.isExternal()) {
continue;
}
IPath rootPath = root.getPath();
int matchingCount = rootPath.matchingFirstSegments(path);
if (matchingCount != 0) {
String name = path.toOSString();
// + 1 is for the File.separatorChar
name = name.substring(rootPath.toOSString().length() + 1, name.length());
name = name.replace(File.separatorChar, '.');
IJavaElement[] list = null;
try {
list = root.getChildren();
} catch (JavaModelException npe) {
continue; // the package fragment root is not present;
}
int elementCount = list.length;
for (int j = 0; j < elementCount; j++) {
IPackageFragment packageFragment = (IPackageFragment) list[j];
if (nameMatches(name, packageFragment, false)) {
if (packageFragment.exists())
return packageFragment;
}
}
}
}
} else {
IJavaElement fromFactory = JavaCore.create(possibleFragment);
if (fromFactory == null) {
return null;
}
if (fromFactory instanceof IPackageFragment) {
return (IPackageFragment) fromFactory;
} else
if (fromFactory instanceof IJavaProject) {
// default package in a default root
JavaProject project = (JavaProject) fromFactory;
try {
IClasspathEntry entry = project.getClasspathEntryFor(path);
if (entry != null) {
IPackageFragmentRoot root =
project.getPackageFragmentRoot(project.getUnderlyingResource());
IPackageFragment[] pkgs = (IPackageFragment[]) fPackageFragments.get(IPackageFragment.DEFAULT_PACKAGE_NAME);
if (pkgs == null) {
return null;
}
for (int i = 0; i < pkgs.length; i++) {
if (pkgs[i].getParent().equals(root)) {
return pkgs[i];
}
}
}
} catch (JavaModelException e) {
return null;
}
}
}
return null;
}
/**
* @see INameLookup
*/
public IPackageFragmentRoot findPackageFragmentRoot(IPath path) {
if (!path.isAbsolute()) {
throw new IllegalArgumentException(Util.bind("path.mustBeAbsolute"/*nonNLS*/));
}
for (int i= 0; i < fPackageFragmentRoots.length; i++) {
IPackageFragmentRoot classpathRoot= fPackageFragmentRoots[i];
if (classpathRoot.getPath().equals(path)) {
return classpathRoot;
}
}
return null;
}
/**
* @see INameLookup
*/
public IPackageFragment[] findPackageFragments(String name, boolean partialMatch) {
int count= fPackageFragmentRoots.length;
if (partialMatch) {
name= name.toLowerCase();
for (int i= 0; i < count; i++) {
IPackageFragmentRoot root= fPackageFragmentRoots[i];
IJavaElement[] list= null;
try {
list= root.getChildren();
} catch (JavaModelException npe) {
continue; // the package fragment root is not present;
}
int elementCount= list.length;
IPackageFragment[] result = new IPackageFragment[elementCount];
int resultLength = 0;
for (int j= 0; j < elementCount; j++) {
IPackageFragment packageFragment= (IPackageFragment) list[j];
if (nameMatches(name, packageFragment, true)) {
if (packageFragment.exists())
result[resultLength++] = packageFragment;
}
}
if (resultLength > 0) {
System.arraycopy(result, 0, result = new IPackageFragment[resultLength], 0, resultLength);
return result;
} else {
return null;
}
}
} else {
// Return only fragments that exists
IPackageFragment[] fragments= (IPackageFragment[]) fPackageFragments.get(name);
if (fragments != null) {
IPackageFragment[] result = new IPackageFragment[fragments.length];
int resultLength = 0;
for (int i= 0; i < fragments.length; i++) {
IPackageFragment packageFragment= fragments[i];
if (packageFragment.exists())
result[resultLength++] = packageFragment;
}
if (resultLength > 0) {
System.arraycopy(result, 0, result = new IPackageFragment[resultLength], 0, resultLength);
return result;
} else {
return null;
}
}
}
return null;
}
/**
*
*/
public IType findType(String typeName, String packageName, boolean partialMatch, int acceptFlags) {
if (packageName == null) {
packageName= IPackageFragment.DEFAULT_PACKAGE_NAME;
}
seekPackageFragments(packageName, false, fgJavaElementRequestor);
IPackageFragment[] packages= fgJavaElementRequestor.getPackageFragments();
fgJavaElementRequestor.reset();
for (int i= 0, length= packages.length; i < length; i++) {
IType type= findType(typeName, packages[i], partialMatch, acceptFlags);
if (type != null)
return type;
}
return null;
}
/**
* @see INameLookup
*/
public IType findType(String name, IPackageFragment pkg, boolean partialMatch, int acceptFlags) {
if (pkg == null) {
return null;
}
// Return first found (ignore duplicates).
seekTypes(name, pkg, partialMatch, acceptFlags, fgSingleTypeRequestor);
IType type= fgSingleTypeRequestor.getType();
fgSingleTypeRequestor.reset();
return type;
}
/**
* @see INameLookup
*/
public IType findType(String name, boolean partialMatch, int acceptFlags) {
int index= name.lastIndexOf('.');
String className= null, packageName= null;
if (index == -1) {
packageName= IPackageFragment.DEFAULT_PACKAGE_NAME;
className= name;
} else {
packageName= name.substring(0, index);
className= name.substring(index + 1);
}
return findType(className, packageName, partialMatch, acceptFlags);
}
/**
* Returns true if the given element's name matches the
* specified <code>searchName</code>, otherwise false.
*
* <p>The <code>partialMatch</code> argument indicates partial matches
* should be considered.
* NOTE: in partialMatch mode, the case will be ignored, and the searchName must already have
* been lowercased.
*/
protected boolean nameMatches(String searchName, IJavaElement element, boolean partialMatch) {
if (partialMatch) {
// partial matches are used in completion mode, thus case insensitive mode
return element.getElementName().toLowerCase().startsWith(searchName);
} else {
return element.getElementName().equals(searchName);
}
}
/**
* @see INameLookup
*/
public void seekPackageFragments(String name, boolean partialMatch, IJavaElementRequestor requestor) {
int count= fPackageFragmentRoots.length;
String matchName= partialMatch ? name.toLowerCase() : name;
for (int i= 0; i < count; i++) {
if (requestor.isCanceled())
return;
IPackageFragmentRoot root= fPackageFragmentRoots[i];
IJavaElement[] list= null;
try {
list= root.getChildren();
} catch (JavaModelException npe) {
continue; // this root package fragment is not present
}
int elementCount= list.length;
for (int j= 0; j < elementCount; j++) {
if (requestor.isCanceled())
return;
IPackageFragment packageFragment= (IPackageFragment) list[j];
if (nameMatches(matchName, packageFragment, partialMatch))
requestor.acceptPackageFragment(packageFragment);
}
}
}
/**
* Notifies the given requestor of all types (classes and interfaces) in the
* given type with the given (possibly qualified) name. Checks
* the requestor at regular intervals to see if the requestor
* has canceled.
*
* @param partialMatch partial name matches qualify when <code>true</code>,
* only exact name matches qualify when <code>false</code>
*/
protected void seekQualifiedMemberTypes(String qualifiedName, IType type, boolean partialMatch, IJavaElementRequestor requestor) {
if (type == null)
return;
IType[] types= null;
try {
types= type.getTypes();
} catch (JavaModelException npe) {
return; // the enclosing type is not present
}
String matchName= qualifiedName;
int index= qualifiedName.indexOf('$');
boolean nested= false;
if (index != -1) {
matchName= qualifiedName.substring(0, index);
nested= true;
}
int length= types.length;
for (int i= 0; i < length; i++) {
if (requestor.isCanceled())
return;
IType memberType= types[i];
if (nameMatches(matchName, memberType, partialMatch))
if (nested) {
seekQualifiedMemberTypes(qualifiedName.substring(index + 1, qualifiedName.length()), memberType, partialMatch, requestor);
} else {
requestor.acceptMemberType(memberType);
}
}
}
/**
* @see INameLookup
*/
public void seekTypes(String name, IPackageFragment pkg, boolean partialMatch, int acceptFlags, IJavaElementRequestor requestor) {
String matchName= partialMatch ? name.toLowerCase() : name;
if (matchName.indexOf('.') >= 0) { //looks for member type A.B
matchName= matchName.replace('.', '$');
}
if (pkg == null) {
findAllTypes(matchName, partialMatch, acceptFlags, requestor);
return;
}
IPackageFragmentRoot root= (IPackageFragmentRoot) pkg.getParent();
try {
int packageFlavor= root.getKind();
switch (packageFlavor) {
case IPackageFragmentRoot.K_BINARY :
seekTypesInBinaryPackage(matchName, pkg, partialMatch, acceptFlags, requestor);
break;
case IPackageFragmentRoot.K_SOURCE :
seekTypesInSourcePackage(matchName, pkg, partialMatch, acceptFlags, requestor);
break;
default :
return;
}
} catch (JavaModelException e) {
return;
}
}
/**
* Performs type search in a binary package.
*/
protected void seekTypesInBinaryPackage(String name, IPackageFragment pkg, boolean partialMatch, int acceptFlags, IJavaElementRequestor requestor) {
IClassFile[] classFiles= null;
try {
classFiles= pkg.getClassFiles();
} catch (JavaModelException npe) {
return; // the package is not present
}
int length= classFiles.length;
String unqualifiedName= name;
int index= name.lastIndexOf('$');
if (index != -1) {
//the type name of the inner type
unqualifiedName= name.substring(index + 1, name.length());
}
String lowerName= name.toLowerCase();
for (int i= 0; i < length; i++) {
if (requestor.isCanceled())
return;
IClassFile classFile= classFiles[i];
/**
* In the following call to nameMatches we must always send true
* for the partialMatch argument since name will never have the
* extension ".class" and the classFile always will.
*/
if (nameMatches(lowerName, classFile, true)) {
IType type= null;
try {
type= classFile.getType();
} catch (JavaModelException npe) {
continue; // the classFile is not present
}
if (!partialMatch || (type.getElementName().length() > 0 && !Character.isDigit(type.getElementName().charAt(0)))) { //not an anonymous type
if (nameMatches(unqualifiedName, type, partialMatch) && acceptType(type, acceptFlags))
requestor.acceptType(type);
}
}
}
}
/**
* Performs type search in a source package.
*/
protected void seekTypesInSourcePackage(String name, IPackageFragment pkg, boolean partialMatch, int acceptFlags, IJavaElementRequestor requestor) {
ICompilationUnit[] compilationUnits= null;
try {
compilationUnits= pkg.getCompilationUnits();
} catch (JavaModelException npe) {
return; // the package is not present
}
int length= compilationUnits.length;
String matchName= name;
int index= name.indexOf('$');
boolean memberType= false;
if (index != -1) {
//the compilation unit name of the inner type
matchName= name.substring(0, index);
memberType= true;
}
/**
* In the following, matchName will never have the extension ".java" and
* the compilationUnits always will. So add it if we're looking for
* an exact match.
*/
String unitName= partialMatch ? matchName.toLowerCase() : matchName + ".java"/*nonNLS*/;
for (int i= 0; i < length; i++) {
if (requestor.isCanceled())
return;
ICompilationUnit compilationUnit= compilationUnits[i];
if (nameMatches(unitName, compilationUnit, partialMatch)) {
IType[] types= null;
try {
types= compilationUnit.getTypes();
} catch (JavaModelException npe) {
continue; // the compilation unit is not present
}
int typeLength= types.length;
for (int j= 0; j < typeLength; j++) {
if (requestor.isCanceled())
return;
IType type= types[j];
if (nameMatches(matchName, type, partialMatch) && acceptType(type, acceptFlags))
if (!memberType) {
requestor.acceptType(type);
} else {
seekQualifiedMemberTypes(name.substring(index + 1, name.length()), type, partialMatch, requestor);
}
}
}
}
}
}