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