fc9fcd8ec6f92990c7dbb0e2ba383aa0224f24c5
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / NameLookup.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2003 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.core;
12
13 import java.io.File;
14 import java.util.ArrayList;
15 import java.util.HashMap;
16 import java.util.Map;
17
18 import net.sourceforge.phpdt.core.IClasspathEntry;
19 import net.sourceforge.phpdt.core.ICompilationUnit;
20 import net.sourceforge.phpdt.core.IJavaElement;
21 import net.sourceforge.phpdt.core.IJavaProject;
22 import net.sourceforge.phpdt.core.IPackageFragment;
23 import net.sourceforge.phpdt.core.IPackageFragmentRoot;
24 import net.sourceforge.phpdt.core.IType;
25 import net.sourceforge.phpdt.core.IWorkingCopy;
26 import net.sourceforge.phpdt.core.JavaCore;
27 import net.sourceforge.phpdt.core.JavaModelException;
28 import net.sourceforge.phpdt.internal.core.util.PerThreadObject;
29 import net.sourceforge.phpdt.internal.core.util.Util;
30
31 import org.eclipse.core.resources.IResource;
32 import org.eclipse.core.resources.IWorkspace;
33 import org.eclipse.core.resources.ResourcesPlugin;
34 import org.eclipse.core.runtime.IPath;
35
36 /**
37  * A <code>NameLookup</code> provides name resolution within a Java project.
38  * The name lookup facility uses the project's classpath to prioritize the order
39  * in which package fragments are searched when resolving a name.
40  * 
41  * <p>
42  * Name lookup only returns a handle when the named element actually exists in
43  * the model; otherwise <code>null</code> is returned.
44  * 
45  * <p>
46  * There are two logical sets of methods within this interface. Methods which
47  * start with <code>find*</code> are intended to be convenience methods for
48  * quickly finding an element within another element; for instance, for finding
49  * a class within a package. The other set of methods all begin with
50  * <code>seek*</code>. These methods do comprehensive searches of the
51  * <code>IJavaProject</code> returning hits in real time through an
52  * <code>IJavaElementRequestor</code>.
53  * 
54  */
55 public class NameLookup {
56         /**
57          * Accept flag for specifying classes.
58          */
59         public static final int ACCEPT_CLASSES = 0x00000002;
60
61         /**
62          * Accept flag for specifying interfaces.
63          */
64         public static final int ACCEPT_INTERFACES = 0x00000004;
65
66         /**
67          * The <code>IPackageFragmentRoot</code>'s associated with the classpath
68          * of this NameLookup facility's project.
69          */
70         protected IPackageFragmentRoot[] fPackageFragmentRoots = null;
71
72         /**
73          * Table that maps package names to lists of package fragments for all
74          * package fragments in the package fragment roots known by this name lookup
75          * facility. To allow > 1 package fragment with the same name, values are
76          * arrays of package fragments ordered as they appear on the classpath.
77          */
78         protected Map fPackageFragments;
79
80         /**
81          * The <code>IWorkspace</code> that this NameLookup is configure within.
82          */
83         protected IWorkspace workspace;
84
85         /**
86          * A map from compilation unit handles to units to look inside (compilation
87          * units or working copies). Allows working copies to take precedence over
88          * compilation units. The cache is a 2-level cache, first keyed by thread.
89          */
90         protected PerThreadObject unitsToLookInside = new PerThreadObject();
91
92         public NameLookup(IJavaProject project) throws JavaModelException {
93                 configureFromProject(project);
94         }
95
96         /**
97          * Returns true if:
98          * <ul>
99          * <li>the given type is an existing class and the flag's
100          * <code>ACCEPT_CLASSES</code> bit is on
101          * <li>the given type is an existing interface and the
102          * <code>ACCEPT_INTERFACES</code> bit is on
103          * <li>neither the <code>ACCEPT_CLASSES</code> or
104          * <code>ACCEPT_INTERFACES</code> bit is on
105          * </ul>
106          * Otherwise, false is returned.
107          */
108         protected boolean acceptType(IType type, int acceptFlags) {
109                 if (acceptFlags == 0)
110                         return true; // no flags, always accepted
111                 try {
112                         if (type.isClass()) {
113                                 return (acceptFlags & ACCEPT_CLASSES) != 0;
114                         } else {
115                                 return (acceptFlags & ACCEPT_INTERFACES) != 0;
116                         }
117                 } catch (JavaModelException npe) {
118                         return false; // the class is not present, do not accept.
119                 }
120         }
121
122         /**
123          * Configures this <code>NameLookup</code> based on the info of the given
124          * <code>IJavaProject</code>.
125          * 
126          * @throws JavaModelException
127          *             if the <code>IJavaProject</code> has no classpath.
128          */
129         private void configureFromProject(IJavaProject project)
130                         throws JavaModelException {
131                 workspace = ResourcesPlugin.getWorkspace();
132                 fPackageFragmentRoots = ((JavaProject) project)
133                                 .getAllPackageFragmentRoots();
134                 fPackageFragments = new HashMap();
135                 IPackageFragment[] frags = this.getPackageFragmentsInRoots(
136                                 fPackageFragmentRoots, project);
137                 for (int i = 0; i < frags.length; i++) {
138                         IPackageFragment fragment = frags[i];
139                         IPackageFragment[] entry = (IPackageFragment[]) fPackageFragments
140                                         .get(fragment.getElementName());
141                         if (entry == null) {
142                                 entry = new IPackageFragment[1];
143                                 entry[0] = fragment;
144                                 fPackageFragments.put(fragment.getElementName(), entry);
145                         } else {
146                                 IPackageFragment[] copy = new IPackageFragment[entry.length + 1];
147                                 System.arraycopy(entry, 0, copy, 0, entry.length);
148                                 copy[entry.length] = fragment;
149                                 fPackageFragments.put(fragment.getElementName(), copy);
150                         }
151                 }
152         }
153
154         /**
155          * Finds every type in the project whose simple name matches the prefix,
156          * informing the requestor of each hit. The requestor is polled for
157          * cancellation at regular intervals.
158          * 
159          * <p>
160          * The <code>partialMatch</code> argument indicates partial matches should
161          * be considered.
162          */
163         private void findAllTypes(String prefix, boolean partialMatch,
164                         int acceptFlags, IJavaElementRequestor requestor) {
165                 int count = fPackageFragmentRoots.length;
166                 for (int i = 0; i < count; i++) {
167                         if (requestor.isCanceled())
168                                 return;
169                         IPackageFragmentRoot root = fPackageFragmentRoots[i];
170                         IJavaElement[] packages = null;
171                         try {
172                                 packages = root.getChildren();
173                         } catch (JavaModelException npe) {
174                                 continue; // the root is not present, continue;
175                         }
176                         if (packages != null) {
177                                 for (int j = 0, packageCount = packages.length; j < packageCount; j++) {
178                                         if (requestor.isCanceled())
179                                                 return;
180                                         seekTypes(prefix, (IPackageFragment) packages[j],
181                                                         partialMatch, acceptFlags, requestor);
182                                 }
183                         }
184                 }
185         }
186
187         /**
188          * Returns the <code>ICompilationUnit</code> which defines the type named
189          * <code>qualifiedTypeName</code>, or <code>null</code> if none exists.
190          * The domain of the search is bounded by the classpath of the
191          * <code>IJavaProject</code> this <code>NameLookup</code> was obtained
192          * from.
193          * <p>
194          * The name must be fully qualified (eg "java.lang.Object",
195          * "java.util.Hashtable$Entry")
196          */
197         public ICompilationUnit findCompilationUnit(String qualifiedTypeName) {
198                 String pkgName = IPackageFragment.DEFAULT_PACKAGE_NAME;
199                 String cuName = qualifiedTypeName;
200
201                 int index = qualifiedTypeName.lastIndexOf('.');
202                 if (index != -1) {
203                         pkgName = qualifiedTypeName.substring(0, index);
204                         cuName = qualifiedTypeName.substring(index + 1);
205                 }
206                 index = cuName.indexOf('$');
207                 if (index != -1) {
208                         cuName = cuName.substring(0, index);
209                 }
210                 cuName += ".java"; //$NON-NLS-1$
211                 IPackageFragment[] frags = (IPackageFragment[]) fPackageFragments
212                                 .get(pkgName);
213                 if (frags != null) {
214                         for (int i = 0; i < frags.length; i++) {
215                                 IPackageFragment frag = frags[i];
216                                 // if (!(frag instanceof JarPackageFragment)) {
217                                 // ICompilationUnit cu= frag.getCompilationUnit(cuName);
218                                 // if (cu != null && cu.exists()) {
219                                 // return cu;
220                                 // }
221                                 // }
222                         }
223                 }
224                 return null;
225         }
226
227         /**
228          * Returns the package fragment whose path matches the given (absolute)
229          * path, or <code>null</code> if none exist. The domain of the search is
230          * bounded by the classpath of the <code>IJavaProject</code> this
231          * <code>NameLookup</code> was obtained from. The path can be: - internal
232          * to the workbench: "/Project/src" - external to the workbench:
233          * "c:/jdk/classes.zip/java/lang"
234          */
235         public IPackageFragment findPackageFragment(IPath path) {
236                 if (!path.isAbsolute()) {
237                         throw new IllegalArgumentException(Util.bind("path.mustBeAbsolute")); //$NON-NLS-1$
238                 }
239                 /*
240                  * this code should rather use the package fragment map to find the
241                  * candidate package, then check if the respective enclosing root maps
242                  * to the one on this given IPath.
243                  */
244                 IResource possibleFragment = workspace.getRoot().findMember(path);
245                 if (possibleFragment == null) {
246                         // external jar
247                         for (int i = 0; i < fPackageFragmentRoots.length; i++) {
248                                 IPackageFragmentRoot root = fPackageFragmentRoots[i];
249                                 if (!root.isExternal()) {
250                                         continue;
251                                 }
252                                 IPath rootPath = root.getPath();
253                                 int matchingCount = rootPath.matchingFirstSegments(path);
254                                 if (matchingCount != 0) {
255                                         String name = path.toOSString();
256                                         // + 1 is for the File.separatorChar
257                                         name = name.substring(rootPath.toOSString().length() + 1,
258                                                         name.length());
259                                         name = name.replace(File.separatorChar, '.');
260                                         IJavaElement[] list = null;
261                                         try {
262                                                 list = root.getChildren();
263                                         } catch (JavaModelException npe) {
264                                                 continue; // the package fragment root is not present;
265                                         }
266                                         int elementCount = list.length;
267                                         for (int j = 0; j < elementCount; j++) {
268                                                 IPackageFragment packageFragment = (IPackageFragment) list[j];
269                                                 if (nameMatches(name, packageFragment, false)) {
270                                                         return packageFragment;
271                                                 }
272                                         }
273                                 }
274                         }
275                 } else {
276                         IJavaElement fromFactory = JavaCore.create(possibleFragment);
277                         if (fromFactory == null) {
278                                 return null;
279                         }
280                         if (fromFactory instanceof IPackageFragment) {
281                                 return (IPackageFragment) fromFactory;
282                         } else if (fromFactory instanceof IJavaProject) {
283                                 // default package in a default root
284                                 JavaProject project = (JavaProject) fromFactory;
285                                 try {
286                                         IClasspathEntry entry = project.getClasspathEntryFor(path);
287                                         if (entry != null) {
288                                                 IPackageFragmentRoot root = project
289                                                                 .getPackageFragmentRoot(project.getResource());
290                                                 IPackageFragment[] pkgs = (IPackageFragment[]) fPackageFragments
291                                                                 .get(IPackageFragment.DEFAULT_PACKAGE_NAME);
292                                                 if (pkgs == null) {
293                                                         return null;
294                                                 }
295                                                 for (int i = 0; i < pkgs.length; i++) {
296                                                         if (pkgs[i].getParent().equals(root)) {
297                                                                 return pkgs[i];
298                                                         }
299                                                 }
300                                         }
301                                 } catch (JavaModelException e) {
302                                         return null;
303                                 }
304                         }
305                 }
306                 return null;
307         }
308
309         /**
310          * Returns the package fragments whose name matches the given (qualified)
311          * name, or <code>null</code> if none exist.
312          * 
313          * The name can be: - empty: "" - qualified: "pack.pack1.pack2"
314          * 
315          * @param partialMatch
316          *            partial name matches qualify when <code>true</code>, only
317          *            exact name matches qualify when <code>false</code>
318          */
319         public IPackageFragment[] findPackageFragments(String name,
320                         boolean partialMatch) {
321                 int count = fPackageFragmentRoots.length;
322                 if (partialMatch) {
323                         name = name.toLowerCase();
324                         for (int i = 0; i < count; i++) {
325                                 IPackageFragmentRoot root = fPackageFragmentRoots[i];
326                                 IJavaElement[] list = null;
327                                 try {
328                                         list = root.getChildren();
329                                 } catch (JavaModelException npe) {
330                                         continue; // the package fragment root is not present;
331                                 }
332                                 int elementCount = list.length;
333                                 IPackageFragment[] result = new IPackageFragment[elementCount];
334                                 int resultLength = 0;
335                                 for (int j = 0; j < elementCount; j++) {
336                                         IPackageFragment packageFragment = (IPackageFragment) list[j];
337                                         if (nameMatches(name, packageFragment, true)) {
338                                                 result[resultLength++] = packageFragment;
339                                         }
340                                 }
341                                 if (resultLength > 0) {
342                                         System.arraycopy(result, 0,
343                                                         result = new IPackageFragment[resultLength], 0,
344                                                         resultLength);
345                                         return result;
346                                 } else {
347                                         return null;
348                                 }
349                         }
350                 } else {
351                         IPackageFragment[] fragments = (IPackageFragment[]) fPackageFragments
352                                         .get(name);
353                         if (fragments != null) {
354                                 IPackageFragment[] result = new IPackageFragment[fragments.length];
355                                 int resultLength = 0;
356                                 for (int i = 0; i < fragments.length; i++) {
357                                         IPackageFragment packageFragment = fragments[i];
358                                         result[resultLength++] = packageFragment;
359                                 }
360                                 if (resultLength > 0) {
361                                         System.arraycopy(result, 0,
362                                                         result = new IPackageFragment[resultLength], 0,
363                                                         resultLength);
364                                         return result;
365                                 } else {
366                                         return null;
367                                 }
368                         }
369                 }
370                 return null;
371         }
372
373         /**
374          * 
375          */
376         public IType findType(String typeName, String packageName,
377                         boolean partialMatch, int acceptFlags) {
378                 if (packageName == null) {
379                         packageName = IPackageFragment.DEFAULT_PACKAGE_NAME;
380                 }
381                 JavaElementRequestor elementRequestor = new JavaElementRequestor();
382                 seekPackageFragments(packageName, false, elementRequestor);
383                 IPackageFragment[] packages = elementRequestor.getPackageFragments();
384
385                 for (int i = 0, length = packages.length; i < length; i++) {
386                         IType type = findType(typeName, packages[i], partialMatch,
387                                         acceptFlags);
388                         if (type != null)
389                                 return type;
390                 }
391                 return null;
392         }
393
394         /**
395          * Returns all the package fragments found in the specified package fragment
396          * roots. Make sure the returned fragments have the given project as great
397          * parent. This ensures the name lookup will not refer to another project
398          * (through jar package fragment roots)
399          */
400         private IPackageFragment[] getPackageFragmentsInRoots(
401                         IPackageFragmentRoot[] roots, IJavaProject project) {
402
403                 // The following code assumes that all the roots have the given project
404                 // as their parent
405                 ArrayList frags = new ArrayList();
406                 for (int i = 0; i < roots.length; i++) {
407                         IPackageFragmentRoot root = roots[i];
408                         try {
409                                 IJavaElement[] children = root.getChildren();
410
411                                 /*
412                                  * 2 jar package fragment roots can be equals but not belonging
413                                  * to the same project. As a result, they share the same element
414                                  * info. So this jar package fragment root could get the
415                                  * children of another jar package fragment root. The following
416                                  * code ensures that the children of this jar package fragment
417                                  * root have the given project as a great parent.
418                                  */
419                                 int length = children.length;
420                                 if (length == 0)
421                                         continue;
422                                 if (children[0].getParent().getParent().equals(project)) {
423                                         // the children have the right parent, simply add them to
424                                         // the list
425                                         for (int j = 0; j < length; j++) {
426                                                 frags.add(children[j]);
427                                         }
428                                 } else {
429                                         // create a new handle with the root as the parent
430                                         for (int j = 0; j < length; j++) {
431                                                 frags.add(root.getPackageFragment(children[j]
432                                                                 .getElementName()));
433                                         }
434                                 }
435                         } catch (JavaModelException e) {
436                                 // do nothing
437                         }
438                 }
439                 IPackageFragment[] fragments = new IPackageFragment[frags.size()];
440                 frags.toArray(fragments);
441                 return fragments;
442         }
443
444         /**
445          * Returns the first type in the given package whose name matches the given
446          * (unqualified) name, or <code>null</code> if none exist. Specifying a
447          * <code>null</code> package will result in no matches. The domain of the
448          * search is bounded by the Java project from which this name lookup was
449          * obtained.
450          * 
451          * @param name
452          *            the name of the type to find
453          * @param pkg
454          *            the package to search
455          * @param partialMatch
456          *            partial name matches qualify when <code>true</code>, only
457          *            exact name matches qualify when <code>false</code>
458          * @param acceptFlags
459          *            a bit mask describing if classes, interfaces or both classes
460          *            and interfaces are desired results. If no flags are specified,
461          *            all types are returned.
462          * 
463          * @see #ACCEPT_CLASSES
464          * @see #ACCEPT_INTERFACES
465          */
466         public IType findType(String name, IPackageFragment pkg,
467                         boolean partialMatch, int acceptFlags) {
468                 if (pkg == null) {
469                         return null;
470                 }
471                 // Return first found (ignore duplicates).
472                 // synchronized(JavaModelManager.getJavaModelManager()){
473                 // SingleTypeRequestor typeRequestor = new SingleTypeRequestor();
474                 // seekTypes(name, pkg, partialMatch, acceptFlags, typeRequestor);
475                 // IType type= typeRequestor.getType();
476                 // return type;
477                 // }
478                 return null;
479         }
480
481         /**
482          * Returns the type specified by the qualified name, or <code>null</code>
483          * if none exist. The domain of the search is bounded by the Java project
484          * from which this name lookup was obtained.
485          * 
486          * @param name
487          *            the name of the type to find
488          * @param partialMatch
489          *            partial name matches qualify when <code>true</code>, only
490          *            exact name matches qualify when <code>false</code>
491          * @param acceptFlags
492          *            a bit mask describing if classes, interfaces or both classes
493          *            and interfaces are desired results. If no flags are specified,
494          *            all types are returned.
495          * 
496          * @see #ACCEPT_CLASSES
497          * @see #ACCEPT_INTERFACES
498          */
499         public IType findType(String name, boolean partialMatch, int acceptFlags) {
500                 int index = name.lastIndexOf('.');
501                 String className = null, packageName = null;
502                 if (index == -1) {
503                         packageName = IPackageFragment.DEFAULT_PACKAGE_NAME;
504                         className = name;
505                 } else {
506                         packageName = name.substring(0, index);
507                         className = name.substring(index + 1);
508                 }
509                 return findType(className, packageName, partialMatch, acceptFlags);
510         }
511
512         /**
513          * Returns true if the given element's name matches the specified
514          * <code>searchName</code>, otherwise false.
515          * 
516          * <p>
517          * The <code>partialMatch</code> argument indicates partial matches should
518          * be considered. NOTE: in partialMatch mode, the case will be ignored, and
519          * the searchName must already have been lowercased.
520          */
521         protected boolean nameMatches(String searchName, IJavaElement element,
522                         boolean partialMatch) {
523                 if (partialMatch) {
524                         // partial matches are used in completion mode, thus case
525                         // insensitive mode
526                         return element.getElementName().toLowerCase()
527                                         .startsWith(searchName);
528                 } else {
529                         return element.getElementName().equals(searchName);
530                 }
531         }
532
533         /**
534          * Notifies the given requestor of all package fragments with the given
535          * name. Checks the requestor at regular intervals to see if the requestor
536          * has canceled. The domain of the search is bounded by the
537          * <code>IJavaProject</code> this <code>NameLookup</code> was obtained
538          * from.
539          * 
540          * @param partialMatch
541          *            partial name matches qualify when <code>true</code>; only
542          *            exact name matches qualify when <code>false</code>
543          */
544         public void seekPackageFragments(String name, boolean partialMatch,
545                         IJavaElementRequestor requestor) {
546                 int count = fPackageFragmentRoots.length;
547                 String matchName = partialMatch ? name.toLowerCase() : name;
548                 for (int i = 0; i < count; i++) {
549                         if (requestor.isCanceled())
550                                 return;
551                         IPackageFragmentRoot root = fPackageFragmentRoots[i];
552                         IJavaElement[] list = null;
553                         try {
554                                 list = root.getChildren();
555                         } catch (JavaModelException npe) {
556                                 continue; // this root package fragment is not present
557                         }
558                         int elementCount = list.length;
559                         for (int j = 0; j < elementCount; j++) {
560                                 if (requestor.isCanceled())
561                                         return;
562                                 IPackageFragment packageFragment = (IPackageFragment) list[j];
563                                 if (nameMatches(matchName, packageFragment, partialMatch))
564                                         requestor.acceptPackageFragment(packageFragment);
565                         }
566                 }
567         }
568
569         /**
570          * Notifies the given requestor of all types (classes and interfaces) in the
571          * given package fragment with the given (unqualified) name. Checks the
572          * requestor at regular intervals to see if the requestor has canceled. If
573          * the given package fragment is <code>null</code>, all types in the
574          * project whose simple name matches the given name are found.
575          * 
576          * @param name
577          *            The name to search
578          * @param pkg
579          *            The corresponding package fragment
580          * @param partialMatch
581          *            partial name matches qualify when <code>true</code>; only
582          *            exact name matches qualify when <code>false</code>
583          * @param acceptFlags
584          *            a bit mask describing if classes, interfaces or both classes
585          *            and interfaces are desired results. If no flags are specified,
586          *            all types are returned.
587          * @param requestor
588          *            The requestor that collects the result
589          * 
590          * @see #ACCEPT_CLASSES
591          * @see #ACCEPT_INTERFACES
592          */
593         public void seekTypes(String name, IPackageFragment pkg,
594                         boolean partialMatch, int acceptFlags,
595                         IJavaElementRequestor requestor) {
596
597                 String matchName = partialMatch ? name.toLowerCase() : name;
598                 if (matchName.indexOf('.') >= 0) { // looks for member type A.B
599                         matchName = matchName.replace('.', '$');
600                 }
601                 if (pkg == null) {
602                         findAllTypes(matchName, partialMatch, acceptFlags, requestor);
603                         return;
604                 }
605                 IPackageFragmentRoot root = (IPackageFragmentRoot) pkg.getParent();
606                 try {
607                         int packageFlavor = root.getKind();
608                         switch (packageFlavor) {
609                         // case IPackageFragmentRoot.K_BINARY :
610                         // seekTypesInBinaryPackage(matchName, pkg, partialMatch,
611                         // acceptFlags, requestor);
612                         // break;
613                         case IPackageFragmentRoot.K_SOURCE:
614                                 seekTypesInSourcePackage(matchName, pkg, partialMatch,
615                                                 acceptFlags, requestor);
616                                 break;
617                         default:
618                                 return;
619                         }
620                 } catch (JavaModelException e) {
621                         return;
622                 }
623         }
624
625         /**
626          * Performs type search in a binary package.
627          */
628         // protected void seekTypesInBinaryPackage(String name, IPackageFragment
629         // pkg, boolean partialMatch, int acceptFlags, IJavaElementRequestor
630         // requestor) {
631         // IClassFile[] classFiles= null;
632         // try {
633         // classFiles= pkg.getClassFiles();
634         // } catch (JavaModelException npe) {
635         // return; // the package is not present
636         // }
637         // int length= classFiles.length;
638         //
639         // String unqualifiedName= name;
640         // int index= name.lastIndexOf('$');
641         // if (index != -1) {
642         // //the type name of the inner type
643         // unqualifiedName= name.substring(index + 1, name.length());
644         // // unqualifiedName is empty if the name ends with a '$' sign.
645         // // See http://dev.eclipse.org/bugs/show_bug.cgi?id=14642
646         // if ((unqualifiedName.length() > 0 &&
647         // Character.isDigit(unqualifiedName.charAt(0))) || unqualifiedName.length()
648         // == 0){
649         // unqualifiedName = name;
650         // }
651         // }
652         // String matchName= partialMatch ? name.toLowerCase() : name;
653         // for (int i= 0; i < length; i++) {
654         // if (requestor.isCanceled())
655         // return;
656         // IClassFile classFile= classFiles[i];
657         // String elementName = classFile.getElementName();
658         // if (partialMatch) elementName = elementName.toLowerCase();
659         //
660         // /**
661         // * Must use startWith because matchName will never have the
662         // * extension ".class" and the elementName always will.
663         // */
664         // if (elementName.startsWith(matchName)) {
665         // IType type= null;
666         // try {
667         // type= classFile.getType();
668         // } catch (JavaModelException npe) {
669         // continue; // the classFile is not present
670         // }
671         // if (!partialMatch || (type.getElementName().length() > 0 &&
672         // !Character.isDigit(type.getElementName().charAt(0)))) { //not an
673         // anonymous type
674         // if (nameMatches(unqualifiedName, type, partialMatch) && acceptType(type,
675         // acceptFlags))
676         // requestor.acceptType(type);
677         // }
678         // }
679         // }
680         // }
681         /**
682          * Performs type search in a source package.
683          */
684         protected void seekTypesInSourcePackage(String name, IPackageFragment pkg,
685                         boolean partialMatch, int acceptFlags,
686                         IJavaElementRequestor requestor) {
687                 ICompilationUnit[] compilationUnits = null;
688                 try {
689                         compilationUnits = pkg.getCompilationUnits();
690                 } catch (JavaModelException npe) {
691                         return; // the package is not present
692                 }
693                 int length = compilationUnits.length;
694                 String matchName = name;
695                 int index = name.indexOf('$');
696                 boolean potentialMemberType = false;
697                 String potentialMatchName = null;
698                 if (index != -1) {
699                         // the compilation unit name of the inner type
700                         potentialMatchName = name.substring(0, index);
701                         potentialMemberType = true;
702                 }
703
704                 /**
705                  * In the following, matchName will never have the extension ".java" and
706                  * the compilationUnits always will. So add it if we're looking for an
707                  * exact match.
708                  */
709                 String unitName = partialMatch ? matchName.toLowerCase() : matchName
710                                 + ".java"; //$NON-NLS-1$
711                 String potentialUnitName = null;
712                 if (potentialMemberType) {
713                         potentialUnitName = partialMatch ? potentialMatchName.toLowerCase()
714                                         : potentialMatchName + ".java"; //$NON-NLS-1$
715                 }
716
717                 for (int i = 0; i < length; i++) {
718                         if (requestor.isCanceled())
719                                 return;
720                         ICompilationUnit compilationUnit = compilationUnits[i];
721
722                         // unit to look inside
723                         ICompilationUnit unitToLookInside = null;
724                         Map workingCopies = (Map) this.unitsToLookInside.getCurrent();
725                         if (workingCopies != null
726                                         && (unitToLookInside = (ICompilationUnit) workingCopies
727                                                         .get(compilationUnit)) != null) {
728                                 compilationUnit = unitToLookInside;
729                         }
730                         if ((unitToLookInside != null && !potentialMemberType)
731                                         || nameMatches(unitName, compilationUnit, partialMatch)) {
732                                 IType[] types = null;
733                                 try {
734                                         types = compilationUnit.getTypes();
735                                 } catch (JavaModelException npe) {
736                                         continue; // the compilation unit is not present
737                                 }
738                                 int typeLength = types.length;
739                                 for (int j = 0; j < typeLength; j++) {
740                                         if (requestor.isCanceled())
741                                                 return;
742                                         IType type = types[j];
743                                         if (nameMatches(matchName, type, partialMatch)) {
744                                                 if (acceptType(type, acceptFlags))
745                                                         requestor.acceptType(type);
746                                         }
747                                 }
748                         } else if (potentialMemberType
749                                         && nameMatches(potentialUnitName, compilationUnit,
750                                                         partialMatch)) {
751                                 IType[] types = null;
752                                 try {
753                                         types = compilationUnit.getTypes();
754                                 } catch (JavaModelException npe) {
755                                         continue; // the compilation unit is not present
756                                 }
757                                 int typeLength = types.length;
758                                 for (int j = 0; j < typeLength; j++) {
759                                         if (requestor.isCanceled())
760                                                 return;
761                                         IType type = types[j];
762                                         if (nameMatches(potentialMatchName, type, partialMatch)) {
763                                                 seekQualifiedMemberTypes(name.substring(index + 1, name
764                                                                 .length()), type, partialMatch, requestor,
765                                                                 acceptFlags);
766                                         }
767                                 }
768                         }
769
770                 }
771         }
772
773         /**
774          * Remembers a set of compilation units that will be looked inside when
775          * looking up a type. If they are working copies, they take precedence of
776          * their compilation units. <code>null</code> means that no special
777          * compilation units should be used.
778          */
779         public void setUnitsToLookInside(IWorkingCopy[] unitsToLookInside) {
780
781                 if (unitsToLookInside == null) {
782                         this.unitsToLookInside.setCurrent(null);
783                 } else {
784                         HashMap workingCopies = new HashMap();
785                         this.unitsToLookInside.setCurrent(workingCopies);
786                         for (int i = 0, length = unitsToLookInside.length; i < length; i++) {
787                                 IWorkingCopy unitToLookInside = unitsToLookInside[i];
788                                 ICompilationUnit original = (ICompilationUnit) unitToLookInside
789                                                 .getOriginalElement();
790                                 if (original != null) {
791                                         workingCopies.put(original, unitToLookInside);
792                                 } else {
793                                         workingCopies.put(unitToLookInside, unitToLookInside);
794                                 }
795                         }
796                 }
797         }
798
799         /**
800          * Notifies the given requestor of all types (classes and interfaces) in the
801          * given type with the given (possibly qualified) name. Checks the requestor
802          * at regular intervals to see if the requestor has canceled.
803          * 
804          * @param partialMatch
805          *            partial name matches qualify when <code>true</code>, only
806          *            exact name matches qualify when <code>false</code>
807          */
808         protected void seekQualifiedMemberTypes(String qualifiedName, IType type,
809                         boolean partialMatch, IJavaElementRequestor requestor,
810                         int acceptFlags) {
811                 if (type == null)
812                         return;
813                 IType[] types = null;
814                 try {
815                         types = type.getTypes();
816                 } catch (JavaModelException npe) {
817                         return; // the enclosing type is not present
818                 }
819                 String matchName = qualifiedName;
820                 int index = qualifiedName.indexOf('$');
821                 boolean nested = false;
822                 if (index != -1) {
823                         matchName = qualifiedName.substring(0, index);
824                         nested = true;
825                 }
826                 int length = types.length;
827                 for (int i = 0; i < length; i++) {
828                         if (requestor.isCanceled())
829                                 return;
830                         IType memberType = types[i];
831                         if (nameMatches(matchName, memberType, partialMatch))
832                                 if (nested) {
833                                         seekQualifiedMemberTypes(qualifiedName.substring(index + 1,
834                                                         qualifiedName.length()), memberType, partialMatch,
835                                                         requestor, acceptFlags);
836                                 } else {
837                                         if (acceptType(memberType, acceptFlags))
838                                                 requestor.acceptMemberType(memberType);
839                                 }
840                 }
841         }
842 }