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