| /******************************************************************************* |
| * 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 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; |
| |
| import java.io.File; |
| import java.util.*; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import org.eclipse.core.resources.*; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IWorkspace; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.jdt.core.IClassFile; |
| import org.eclipse.jdt.core.IClasspathEntry; |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.IPackageFragment; |
| import org.eclipse.jdt.core.IPackageFragmentRoot; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.search.*; |
| import org.eclipse.jdt.core.search.IJavaSearchConstants; |
| import org.eclipse.jdt.core.search.ITypeNameRequestor; |
| import org.eclipse.jdt.core.search.SearchEngine; |
| import org.eclipse.jdt.internal.compiler.util.SuffixConstants; |
| import org.eclipse.jdt.internal.core.util.Util; |
| |
| /** |
| * A <code>NameLookup</code> provides name resolution within a Java project. |
| * The name lookup facility uses the project's classpath to prioritize the |
| * order in which package fragments are searched when resolving a name. |
| * |
| * <p>Name lookup only returns a handle when the named element actually |
| * exists in the model; otherwise <code>null</code> is returned. |
| * |
| * <p>There are two logical sets of methods within this interface. Methods |
| * which start with <code>find*</code> are intended to be convenience methods for quickly |
| * finding an element within another element; for instance, for finding a class within a |
| * package. The other set of methods all begin with <code>seek*</code>. These methods |
| * do comprehensive searches of the <code>IJavaProject</code> returning hits |
| * in real time through an <code>IJavaElementRequestor</code>. |
| * |
| */ |
| public class NameLookup implements SuffixConstants { |
| /** |
| * Accept flag for specifying classes. |
| */ |
| public static final int ACCEPT_CLASSES = 0x00000002; |
| |
| /** |
| * Accept flag for specifying interfaces. |
| */ |
| public static final int ACCEPT_INTERFACES = 0x00000004; |
| |
| /** |
| * The <code>IPackageFragmentRoot</code>'s associated |
| * with the classpath of this NameLookup facility's |
| * project. |
| */ |
| protected IPackageFragmentRoot[] packageFragmentRoots; |
| |
| /** |
| * 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 Map packageFragments; |
| |
| /** |
| * A map from compilation unit handles to units to look inside (compilation |
| * units or working copies). |
| * Allows working copies to take precedence over compilation units. |
| */ |
| protected HashMap unitsToLookInside; |
| |
| public NameLookup(IPackageFragmentRoot[] packageFragmentRoots, HashMap packageFragments, ICompilationUnit[] workingCopies) { |
| this.packageFragmentRoots = packageFragmentRoots; |
| this.packageFragments = packageFragments; |
| if (workingCopies != null) { |
| this.unitsToLookInside = new HashMap(); |
| for (int i = 0, length = workingCopies.length; i < length; i++) { |
| ICompilationUnit unitToLookInside = workingCopies[i]; |
| ICompilationUnit original = unitToLookInside.getPrimary(); |
| this.unitsToLookInside.put(original, unitToLookInside); |
| } |
| } |
| } |
| |
| /** |
| * 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. |
| } |
| } |
| |
| /** |
| * 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= this.packageFragmentRoots.length; |
| for (int i= 0; i < count; i++) { |
| if (requestor.isCanceled()) |
| return; |
| IPackageFragmentRoot root= this.packageFragmentRoots[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); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Returns the <code>ICompilationUnit</code> which defines the type |
| * named <code>qualifiedTypeName</code>, or <code>null</code> if |
| * none exists. The domain of the search is bounded by the classpath |
| * of the <code>IJavaProject</code> this <code>NameLookup</code> was |
| * obtained from. |
| * <p> |
| * The name must be fully qualified (eg "java.lang.Object", "java.util.Hashtable$Entry") |
| */ |
| 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 += SUFFIX_STRING_java; |
| IPackageFragment[] frags= (IPackageFragment[]) this.packageFragments.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; |
| } |
| |
| /** |
| * Returns the package fragment whose path matches the given |
| * (absolute) path, or <code>null</code> if none exist. The domain of |
| * the search is bounded by the classpath of the <code>IJavaProject</code> |
| * this <code>NameLookup</code> was obtained from. |
| * The path can be: |
| * - internal to the workbench: "/Project/src" |
| * - external to the workbench: "c:/jdk/classes.zip/java/lang" |
| */ |
| public IPackageFragment findPackageFragment(IPath path) { |
| if (!path.isAbsolute()) { |
| throw new IllegalArgumentException(Util.bind("path.mustBeAbsolute")); //$NON-NLS-1$ |
| } |
| /* |
| * this code should rather use the package fragment map to find the candidate package, then |
| * check if the respective enclosing root maps to the one on this given IPath. |
| */ |
| IResource possibleFragment = ResourcesPlugin.getWorkspace().getRoot().findMember(path); |
| if (possibleFragment == null) { |
| //external jar |
| for (int i = 0; i < this.packageFragmentRoots.length; i++) { |
| IPackageFragmentRoot root = this.packageFragmentRoots[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)) { |
| 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.getResource()); |
| IPackageFragment[] pkgs = (IPackageFragment[]) this.packageFragments.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; |
| } |
| |
| /** |
| * Returns the package fragments whose name matches the given |
| * (qualified) name, or <code>null</code> if none exist. |
| * |
| * The name can be: |
| * - empty: "" |
| * - qualified: "pack.pack1.pack2" |
| * @param partialMatch partial name matches qualify when <code>true</code>, |
| * only exact name matches qualify when <code>false</code> |
| */ |
| public IPackageFragment[] findPackageFragments(String name, boolean partialMatch) { |
| int count= this.packageFragmentRoots.length; |
| if (partialMatch) { |
| name= name.toLowerCase(); |
| for (int i= 0; i < count; i++) { |
| IPackageFragmentRoot root= this.packageFragmentRoots[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)) { |
| result[resultLength++] = packageFragment; |
| } |
| } |
| if (resultLength > 0) { |
| System.arraycopy(result, 0, result = new IPackageFragment[resultLength], 0, resultLength); |
| return result; |
| } else { |
| return null; |
| } |
| } |
| } else { |
| IPackageFragment[] fragments= (IPackageFragment[]) this.packageFragments.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]; |
| 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.length() == 0) { |
| packageName= IPackageFragment.DEFAULT_PACKAGE_NAME; |
| } else if (typeName.length() > 0 && Character.isLowerCase(typeName.charAt(0))) { |
| // see if this is a known package and not a type |
| if (findPackageFragments(packageName + "." + typeName, false) != null) return null; //$NON-NLS-1$ |
| } |
| JavaElementRequestor elementRequestor = new JavaElementRequestor(); |
| seekPackageFragments(packageName, false, elementRequestor); |
| IPackageFragment[] packages= elementRequestor.getPackageFragments(); |
| |
| 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; |
| } |
| |
| /** |
| * Returns the first type in the given package whose name |
| * matches the given (unqualified) name, or <code>null</code> if none |
| * exist. Specifying a <code>null</code> package will result in no matches. |
| * The domain of the search is bounded by the Java project from which |
| * this name lookup was obtained. |
| * |
| * @param name the name of the type to find |
| * @param pkg the package to search |
| * @param partialMatch partial name matches qualify when <code>true</code>, |
| * only exact name matches qualify when <code>false</code> |
| * @param acceptFlags a bit mask describing if classes, interfaces or both classes and interfaces |
| * are desired results. If no flags are specified, all types are returned. |
| * |
| * @see #ACCEPT_CLASSES |
| * @see #ACCEPT_INTERFACES |
| */ |
| public IType findType(String name, IPackageFragment pkg, boolean partialMatch, int acceptFlags) { |
| if (pkg == null) return null; |
| |
| // Return first found (ignore duplicates). |
| SingleTypeRequestor typeRequestor = new SingleTypeRequestor(); |
| seekTypes(name, pkg, partialMatch, acceptFlags, typeRequestor); |
| IType type = typeRequestor.getType(); |
| // if (type == null) |
| // type = findSecondaryType(name, pkg, partialMatch, acceptFlags); |
| return type; |
| } |
| |
| // TODO (kent) enable once index support is in |
| IType findSecondaryType(String typeName, IPackageFragment pkg, boolean partialMatch, final int acceptFlags) { |
| try { |
| final ArrayList paths = new ArrayList(); |
| ITypeNameRequestor nameRequestor = new ITypeNameRequestor() { |
| public void acceptClass(char[] packageName, char[] simpleTypeName, char[][] enclosingTypeNames, String path) { |
| if ((acceptFlags & ACCEPT_CLASSES) != 0) |
| if (enclosingTypeNames == null || enclosingTypeNames.length == 0) // accept only top level types |
| paths.add(path); |
| } |
| public void acceptInterface(char[] packageName, char[] simpleTypeName, char[][] enclosingTypeNames, String path) { |
| if ((acceptFlags & ACCEPT_INTERFACES) != 0) |
| if (enclosingTypeNames == null || enclosingTypeNames.length == 0) // accept only top level types |
| paths.add(path); |
| } |
| }; |
| |
| int matchMode = partialMatch ? SearchPattern.R_PREFIX_MATCH : SearchPattern.R_EXACT_MATCH; |
| int matchRule = !partialMatch ? matchMode | SearchPattern.R_CASE_SENSITIVE : matchMode; |
| new SearchEngine().searchAllTypeNames( |
| pkg.getElementName().toCharArray(), |
| typeName.toCharArray(), |
| matchRule, |
| IJavaSearchConstants.TYPE, |
| SearchEngine.createJavaSearchScope(new IJavaElement[] {pkg}, false), |
| nameRequestor, |
| IJavaSearchConstants.CANCEL_IF_NOT_READY_TO_SEARCH, |
| null); |
| |
| if (!paths.isEmpty()) { |
| IWorkspace workspace = ResourcesPlugin.getWorkspace(); |
| for (int i = 0, l = paths.size(); i < l; i++) { |
| String pathname = (String) paths.get(i); |
| if (org.eclipse.jdt.internal.compiler.util.Util.isJavaFileName(pathname)) { |
| IFile file = workspace.getRoot().getFile(new Path(pathname)); |
| ICompilationUnit unit = JavaCore.createCompilationUnitFrom(file); |
| return unit.getType(typeName); |
| } |
| } |
| } |
| } catch (JavaModelException e) { |
| // ignore |
| } catch (OperationCanceledException ignore) { |
| // ignore |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the type specified by the qualified name, or <code>null</code> |
| * if none exist. The domain of |
| * the search is bounded by the Java project from which this name lookup was obtained. |
| * |
| * @param name the name of the type to find |
| * @param partialMatch partial name matches qualify when <code>true</code>, |
| * only exact name matches qualify when <code>false</code> |
| * @param acceptFlags a bit mask describing if classes, interfaces or both classes and interfaces |
| * are desired results. If no flags are specified, all types are returned. |
| * |
| * @see #ACCEPT_CLASSES |
| * @see #ACCEPT_INTERFACES |
| */ |
| 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); |
| } |
| } |
| |
| /** |
| * Notifies the given requestor of all package fragments with the |
| * given name. Checks the requestor at regular intervals to see if the |
| * requestor has canceled. The domain of |
| * the search is bounded by the <code>IJavaProject</code> |
| * this <code>NameLookup</code> was obtained from. |
| * |
| * @param partialMatch partial name matches qualify when <code>true</code>; |
| * only exact name matches qualify when <code>false</code> |
| */ |
| public void seekPackageFragments(String name, boolean partialMatch, IJavaElementRequestor requestor) { |
| int count= this.packageFragmentRoots.length; |
| String matchName= partialMatch ? name.toLowerCase() : name; |
| for (int i= 0; i < count; i++) { |
| if (requestor.isCanceled()) |
| return; |
| IPackageFragmentRoot root= this.packageFragmentRoots[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 package fragment with the given (unqualified) name. |
| * Checks the requestor at regular intervals to see if the requestor |
| * has canceled. If the given package fragment is <code>null</code>, all types in the |
| * project whose simple name matches the given name are found. |
| * |
| * @param name The name to search |
| * @param pkg The corresponding package fragment |
| * @param partialMatch partial name matches qualify when <code>true</code>; |
| * only exact name matches qualify when <code>false</code> |
| * @param acceptFlags a bit mask describing if classes, interfaces or both classes and interfaces |
| * are desired results. If no flags are specified, all types are returned. |
| * @param requestor The requestor that collects the result |
| * |
| * @see #ACCEPT_CLASSES |
| * @see #ACCEPT_INTERFACES |
| */ |
| 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()); |
| // unqualifiedName is empty if the name ends with a '$' sign. |
| // See http://dev.eclipse.org/bugs/show_bug.cgi?id=14642 |
| if ((unqualifiedName.length() > 0 && Character.isDigit(unqualifiedName.charAt(0))) || unqualifiedName.length() == 0){ |
| unqualifiedName = name; |
| } |
| } |
| String matchName= partialMatch ? name.toLowerCase() : name; |
| for (int i= 0; i < length; i++) { |
| if (requestor.isCanceled()) |
| return; |
| IClassFile classFile= classFiles[i]; |
| String elementName = classFile.getElementName(); |
| if (partialMatch) elementName = elementName.toLowerCase(); |
| |
| /** |
| * Must use startWith because matchName will never have the |
| * extension ".class" and the elementName always will. |
| */ |
| if (elementName.startsWith(matchName)) { |
| 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 |
| } |
| |
| // replace with working copies to look inside |
| int length= compilationUnits.length; |
| boolean[] isWorkingCopy = new boolean[length]; |
| int workingCopiesSize; |
| if (this.unitsToLookInside != null && (workingCopiesSize = this.unitsToLookInside.size()) > 0) { |
| Map temp = new HashMap(workingCopiesSize); |
| temp.putAll(this.unitsToLookInside); |
| for (int i = 0; i < length; i++) { |
| ICompilationUnit unit = compilationUnits[i]; |
| ICompilationUnit workingCopy = (ICompilationUnit)temp.remove(unit); |
| if (workingCopy != null) { |
| compilationUnits[i] = workingCopy; |
| isWorkingCopy[i] = true; |
| } |
| } |
| // add remaining working copies that belong to this package |
| int index = 0; |
| Collection values = temp.values(); |
| Iterator iterator = values.iterator(); |
| while (iterator.hasNext()) { |
| ICompilationUnit workingCopy = (ICompilationUnit)iterator.next(); |
| if (pkg.equals(workingCopy.getParent())) { |
| if (index == 0) { |
| int valuesLength = values.size(); |
| index = length; |
| length += valuesLength; |
| System.arraycopy(compilationUnits, 0, compilationUnits = new ICompilationUnit[length], 0, index); |
| System.arraycopy(isWorkingCopy, 0, isWorkingCopy = new boolean[length], 0, index); |
| } |
| isWorkingCopy[index] = true; |
| compilationUnits[index++] = workingCopy; |
| } |
| } |
| if (index > 0 && index < length) { |
| System.arraycopy(compilationUnits, 0, compilationUnits = new ICompilationUnit[index], 0, index); |
| System.arraycopy(isWorkingCopy, 0, isWorkingCopy = new boolean[index], 0, index); |
| length = index; |
| } |
| } |
| |
| String matchName = name; |
| int index= name.indexOf('$'); |
| boolean potentialMemberType = false; |
| String potentialMatchName = null; |
| if (index != -1) { |
| //the compilation unit name of the inner type |
| potentialMatchName = name.substring(0, index); |
| potentialMemberType = 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 + SUFFIX_STRING_java; |
| String potentialUnitName = null; |
| if (potentialMemberType) { |
| potentialUnitName = partialMatch ? potentialMatchName.toLowerCase() : potentialMatchName + SUFFIX_STRING_java; |
| } |
| |
| for (int i= 0; i < length; i++) { |
| if (requestor.isCanceled()) |
| return; |
| ICompilationUnit compilationUnit= compilationUnits[i]; |
| |
| if ((isWorkingCopy[i] && !potentialMemberType) |
| || 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)) { |
| if (acceptType(type, acceptFlags)) requestor.acceptType(type); |
| } |
| } |
| } else if (potentialMemberType && nameMatches(potentialUnitName, 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(potentialMatchName, type, partialMatch)) { |
| seekQualifiedMemberTypes(name.substring(index + 1, name.length()), type, partialMatch, requestor, acceptFlags); |
| } |
| } |
| } |
| |
| } |
| } |
| |
| /** |
| * 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, int acceptFlags) { |
| 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, acceptFlags); |
| } else { |
| if (acceptType(memberType, acceptFlags)) requestor.acceptMemberType(memberType); |
| } |
| } |
| } |
| } |