KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > core > hierarchy > IndexBasedHierarchyBuilder


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jdt.internal.core.hierarchy;
12
13 import java.util.*;
14
15 import org.eclipse.core.resources.IFile;
16 import org.eclipse.core.resources.IResource;
17 import org.eclipse.core.runtime.IPath;
18 import org.eclipse.core.runtime.IProgressMonitor;
19 import org.eclipse.core.runtime.NullProgressMonitor;
20 import org.eclipse.core.runtime.SubProgressMonitor;
21 import org.eclipse.jdt.core.*;
22 import org.eclipse.jdt.core.compiler.CharOperation;
23 import org.eclipse.jdt.core.search.*;
24 import org.eclipse.jdt.internal.compiler.env.AccessRuleSet;
25 import org.eclipse.jdt.internal.compiler.env.IBinaryType;
26 import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
27 import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
28 import org.eclipse.jdt.internal.compiler.util.HashtableOfObject;
29 import org.eclipse.jdt.internal.compiler.util.HashtableOfObjectToInt;
30 import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
31 import org.eclipse.jdt.internal.core.*;
32 import org.eclipse.jdt.internal.core.search.IndexQueryRequestor;
33 import org.eclipse.jdt.internal.core.search.JavaSearchParticipant;
34 import org.eclipse.jdt.internal.core.search.SubTypeSearchJob;
35 import org.eclipse.jdt.internal.core.search.indexing.IIndexConstants;
36 import org.eclipse.jdt.internal.core.search.indexing.IndexManager;
37 import org.eclipse.jdt.internal.core.search.matching.MatchLocator;
38 import org.eclipse.jdt.internal.core.search.matching.SuperTypeReferencePattern;
39 import org.eclipse.jdt.internal.core.util.HandleFactory;
40 import org.eclipse.jdt.internal.core.util.Util;
41
42 public class IndexBasedHierarchyBuilder extends HierarchyBuilder implements SuffixConstants {
43     public static final int MAXTICKS = 800; // heuristic so that there still progress for deep hierachies
44
/**
45      * A temporary cache of compilation units to handles to speed info
46      * to handle translation - it only contains the entries
47      * for the types in the region (in other words, it contains no supertypes outside
48      * the region).
49      */

50     protected Map cuToHandle;
51
52     /**
53      * The scope this hierarchy builder should restrain results to.
54      */

55     protected IJavaSearchScope scope;
56
57     /**
58      * Cache used to record binaries recreated from index matches
59      */

60     protected Map binariesFromIndexMatches;
61     
62     /**
63      * Collection used to queue subtype index queries
64      */

65     static class Queue {
66         public char[][] names = new char[10][];
67         public int start = 0;
68         public int end = -1;
69         public void add(char[] name){
70             if (++this.end == this.names.length){
71                 this.end -= this.start;
72                 System.arraycopy(this.names, this.start, this.names = new char[this.end*2][], 0, this.end);
73                 this.start = 0;
74             }
75             this.names[this.end] = name;
76         }
77         public char[] retrieve(){
78             if (this.start > this.end) return null; // none
79

80             char[] name = this.names[this.start++];
81             if (this.start > this.end){
82                 this.start = 0;
83                 this.end = -1;
84             }
85             return name;
86         }
87         public String JavaDoc toString(){
88             StringBuffer JavaDoc buffer = new StringBuffer JavaDoc("Queue:\n"); //$NON-NLS-1$
89
for (int i = this.start; i <= this.end; i++){
90                 buffer.append(this.names[i]).append('\n');
91             }
92             return buffer.toString();
93         }
94     }
95 public IndexBasedHierarchyBuilder(TypeHierarchy hierarchy, IJavaSearchScope scope) throws JavaModelException {
96     super(hierarchy);
97     this.cuToHandle = new HashMap(5);
98     this.binariesFromIndexMatches = new HashMap(10);
99     this.scope = scope;
100 }
101 public void build(boolean computeSubtypes) {
102     JavaModelManager manager = JavaModelManager.getJavaModelManager();
103     try {
104         // optimize access to zip files while building hierarchy
105
manager.cacheZipFiles();
106                 
107         if (computeSubtypes) {
108             // Note by construction there always is a focus type here
109
IType focusType = getType();
110             boolean focusIsObject = focusType.getElementName().equals(new String JavaDoc(IIndexConstants.OBJECT));
111             int amountOfWorkForSubtypes = focusIsObject ? 5 : 80; // percentage of work needed to get possible subtypes
112
IProgressMonitor possibleSubtypesMonitor =
113                 this.hierarchy.progressMonitor == null ?
114                     null :
115                     new SubProgressMonitor(this.hierarchy.progressMonitor, amountOfWorkForSubtypes);
116             HashSet localTypes = new HashSet(10); // contains the paths that have potential subtypes that are local/anonymous types
117
String JavaDoc[] allPossibleSubtypes;
118             if (((Member)focusType).getOuterMostLocalContext() == null) {
119                 // top level or member type
120
allPossibleSubtypes = this.determinePossibleSubTypes(localTypes, possibleSubtypesMonitor);
121             } else {
122                 // local or anonymous type
123
allPossibleSubtypes = CharOperation.NO_STRINGS;
124             }
125             if (allPossibleSubtypes != null) {
126                 IProgressMonitor buildMonitor =
127                     this.hierarchy.progressMonitor == null ?
128                         null :
129                         new SubProgressMonitor(this.hierarchy.progressMonitor, 100 - amountOfWorkForSubtypes);
130                 this.hierarchy.initialize(allPossibleSubtypes.length);
131                 buildFromPotentialSubtypes(allPossibleSubtypes, localTypes, buildMonitor);
132             }
133         } else {
134             this.hierarchy.initialize(1);
135             this.buildSupertypes();
136         }
137     } finally {
138         manager.flushZipFiles();
139     }
140 }
141 private void buildForProject(JavaProject project, ArrayList potentialSubtypes, org.eclipse.jdt.core.ICompilationUnit[] workingCopies, HashSet localTypes, IProgressMonitor monitor) throws JavaModelException {
142     // resolve
143
int openablesLength = potentialSubtypes.size();
144     if (openablesLength > 0) {
145         // copy vectors into arrays
146
Openable[] openables = new Openable[openablesLength];
147         potentialSubtypes.toArray(openables);
148
149         // sort in the order of roots and in reverse alphabetical order for .class file
150
// since requesting top level types in the process of caching an enclosing type is
151
// not supported by the lookup environment
152
IPackageFragmentRoot[] roots = project.getPackageFragmentRoots();
153         int rootsLength = roots.length;
154         final HashtableOfObjectToInt indexes = new HashtableOfObjectToInt(openablesLength);
155         for (int i = 0; i < openablesLength; i++) {
156             IJavaElement root = openables[i].getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
157             int index;
158             for (index = 0; index < rootsLength; index++) {
159                 if (roots[index].equals(root))
160                     break;
161             }
162             indexes.put(openables[i], index);
163         }
164         Arrays.sort(openables, new Comparator() {
165             public int compare(Object JavaDoc a, Object JavaDoc b) {
166                 int aIndex = indexes.get(a);
167                 int bIndex = indexes.get(b);
168                 if (aIndex != bIndex)
169                     return aIndex - bIndex;
170                 return ((Openable) b).getElementName().compareTo(((Openable) a).getElementName());
171             }
172         });
173         
174         IType focusType = this.getType();
175         boolean inProjectOfFocusType = focusType != null && focusType.getJavaProject().equals(project);
176         org.eclipse.jdt.core.ICompilationUnit[] unitsToLookInside = null;
177         if (inProjectOfFocusType) {
178             org.eclipse.jdt.core.ICompilationUnit unitToLookInside = focusType.getCompilationUnit();
179             if (unitToLookInside != null) {
180                 int wcLength = workingCopies == null ? 0 : workingCopies.length;
181                 if (wcLength == 0) {
182                     unitsToLookInside = new org.eclipse.jdt.core.ICompilationUnit[] {unitToLookInside};
183                 } else {
184                     unitsToLookInside = new org.eclipse.jdt.core.ICompilationUnit[wcLength+1];
185                     unitsToLookInside[0] = unitToLookInside;
186                     System.arraycopy(workingCopies, 0, unitsToLookInside, 1, wcLength);
187                 }
188             } else {
189                 unitsToLookInside = workingCopies;
190             }
191         }
192
193         SearchableEnvironment searchableEnvironment = project.newSearchableNameEnvironment(unitsToLookInside);
194         this.nameLookup = searchableEnvironment.nameLookup;
195         Map options = project.getOptions(true);
196         // disable task tags to speed up parsing
197
options.put(JavaCore.COMPILER_TASK_TAGS, ""); //$NON-NLS-1$
198
this.hierarchyResolver =
199             new HierarchyResolver(searchableEnvironment, options, this, new DefaultProblemFactory());
200         if (focusType != null) {
201             Member declaringMember = ((Member)focusType).getOuterMostLocalContext();
202             if (declaringMember == null) {
203                 // top level or member type
204
if (!inProjectOfFocusType) {
205                     char[] typeQualifiedName = focusType.getTypeQualifiedName('.').toCharArray();
206                     String JavaDoc[] packageName = ((PackageFragment) focusType.getPackageFragment()).names;
207                     if (searchableEnvironment.findType(typeQualifiedName, Util.toCharArrays(packageName)) == null) {
208                         // focus type is not visible in this project: no need to go further
209
return;
210                     }
211                 }
212             } else {
213                 // local or anonymous type
214
Openable openable;
215                 if (declaringMember.isBinary()) {
216                     openable = (Openable)declaringMember.getClassFile();
217                 } else {
218                     openable = (Openable)declaringMember.getCompilationUnit();
219                 }
220                 localTypes = new HashSet();
221                 localTypes.add(openable.getPath().toString());
222                 this.hierarchyResolver.resolve(new Openable[] {openable}, localTypes, monitor);
223                 return;
224             }
225         }
226         this.hierarchyResolver.resolve(openables, localTypes, monitor);
227     }
228 }
229 /**
230  * Configure this type hierarchy based on the given potential subtypes.
231  */

232 private void buildFromPotentialSubtypes(String JavaDoc[] allPotentialSubTypes, HashSet localTypes, IProgressMonitor monitor) {
233     IType focusType = this.getType();
234         
235     // substitute compilation units with working copies
236
HashMap wcPaths = new HashMap(); // a map from path to working copies
237
int wcLength;
238     org.eclipse.jdt.core.ICompilationUnit[] workingCopies = this.hierarchy.workingCopies;
239     if (workingCopies != null && (wcLength = workingCopies.length) > 0) {
240         String JavaDoc[] newPaths = new String JavaDoc[wcLength];
241         for (int i = 0; i < wcLength; i++) {
242             org.eclipse.jdt.core.ICompilationUnit workingCopy = workingCopies[i];
243             String JavaDoc path = workingCopy.getPath().toString();
244             wcPaths.put(path, workingCopy);
245             newPaths[i] = path;
246         }
247         int potentialSubtypesLength = allPotentialSubTypes.length;
248         System.arraycopy(allPotentialSubTypes, 0, allPotentialSubTypes = new String JavaDoc[potentialSubtypesLength+wcLength], 0, potentialSubtypesLength);
249         System.arraycopy(newPaths, 0, allPotentialSubTypes, potentialSubtypesLength, wcLength);
250     }
251             
252     int length = allPotentialSubTypes.length;
253
254     // inject the compilation unit of the focus type (so that types in
255
// this cu have special visibility permission (this is also usefull
256
// when the cu is a working copy)
257
Openable focusCU = (Openable)focusType.getCompilationUnit();
258     String JavaDoc focusPath = null;
259     if (focusCU != null) {
260         focusPath = focusCU.getPath().toString();
261         if (length > 0) {
262             System.arraycopy(allPotentialSubTypes, 0, allPotentialSubTypes = new String JavaDoc[length+1], 0, length);
263             allPotentialSubTypes[length] = focusPath;
264         } else {
265             allPotentialSubTypes = new String JavaDoc[] {focusPath};
266         }
267         length++;
268     }
269     
270     /*
271      * Sort in alphabetical order so that potential subtypes are grouped per project
272      */

273     Arrays.sort(allPotentialSubTypes);
274
275     ArrayList potentialSubtypes = new ArrayList();
276     try {
277         // create element infos for subtypes
278
HandleFactory factory = new HandleFactory();
279         IJavaProject currentProject = null;
280         if (monitor != null) monitor.beginTask("", length*2 /* 1 for build binding, 1 for connect hierarchy*/); //$NON-NLS-1$
281
for (int i = 0; i < length; i++) {
282             try {
283                 String JavaDoc resourcePath = allPotentialSubTypes[i];
284                 
285                 // skip duplicate paths (e.g. if focus path was injected when it was already a potential subtype)
286
if (i > 0 && resourcePath.equals(allPotentialSubTypes[i-1])) continue;
287                 
288                 Openable handle;
289                 org.eclipse.jdt.core.ICompilationUnit workingCopy = (org.eclipse.jdt.core.ICompilationUnit)wcPaths.get(resourcePath);
290                 if (workingCopy != null) {
291                     handle = (Openable)workingCopy;
292                 } else {
293                     handle =
294                         resourcePath.equals(focusPath) ?
295                             focusCU :
296                             factory.createOpenable(resourcePath, this.scope);
297                     if (handle == null) continue; // match is outside classpath
298
}
299                 
300                 IJavaProject project = handle.getJavaProject();
301                 if (currentProject == null) {
302                     currentProject = project;
303                     potentialSubtypes = new ArrayList(5);
304                 } else if (!currentProject.equals(project)) {
305                     // build current project
306
this.buildForProject((JavaProject)currentProject, potentialSubtypes, workingCopies, localTypes, monitor);
307                     currentProject = project;
308                     potentialSubtypes = new ArrayList(5);
309                 }
310                 
311                 potentialSubtypes.add(handle);
312             } catch (JavaModelException e) {
313                 continue;
314             }
315         }
316         
317         // build last project
318
try {
319             if (currentProject == null) {
320                 // case of no potential subtypes
321
currentProject = focusType.getJavaProject();
322                 if (focusType.isBinary()) {
323                     potentialSubtypes.add(focusType.getClassFile());
324                 } else {
325                     potentialSubtypes.add(focusType.getCompilationUnit());
326                 }
327             }
328             this.buildForProject((JavaProject)currentProject, potentialSubtypes, workingCopies, localTypes, monitor);
329         } catch (JavaModelException e) {
330             // ignore
331
}
332         
333         // Compute hierarchy of focus type if not already done (case of a type with potential subtypes that are not real subtypes)
334
if (!this.hierarchy.contains(focusType)) {
335             try {
336                 currentProject = focusType.getJavaProject();
337                 potentialSubtypes = new ArrayList();
338                 if (focusType.isBinary()) {
339                     potentialSubtypes.add(focusType.getClassFile());
340                 } else {
341                     potentialSubtypes.add(focusType.getCompilationUnit());
342                 }
343                 this.buildForProject((JavaProject)currentProject, potentialSubtypes, workingCopies, localTypes, monitor);
344             } catch (JavaModelException e) {
345                 // ignore
346
}
347         }
348         
349         // Add focus if not already in (case of a type with no explicit super type)
350
if (!this.hierarchy.contains(focusType)) {
351             this.hierarchy.addRootClass(focusType);
352         }
353     } finally {
354         if (monitor != null) monitor.done();
355     }
356 }
357 protected ICompilationUnit createCompilationUnitFromPath(Openable handle, IFile file) {
358     ICompilationUnit unit = super.createCompilationUnitFromPath(handle, file);
359     this.cuToHandle.put(unit, handle);
360     return unit;
361 }
362 protected IBinaryType createInfoFromClassFile(Openable classFile, IResource file) {
363     String JavaDoc documentPath = classFile.getPath().toString();
364     IBinaryType binaryType = (IBinaryType)this.binariesFromIndexMatches.get(documentPath);
365     if (binaryType != null) {
366         this.infoToHandle.put(binaryType, classFile);
367         return binaryType;
368     } else {
369         return super.createInfoFromClassFile(classFile, file);
370     }
371 }
372 protected IBinaryType createInfoFromClassFileInJar(Openable classFile) {
373     String JavaDoc filePath = (((ClassFile)classFile).getType().getFullyQualifiedName('$')).replace('.', '/') + SuffixConstants.SUFFIX_STRING_class;
374     IPackageFragmentRoot root = classFile.getPackageFragmentRoot();
375     IPath path = root.getPath();
376     // take the OS path for external jars, and the forward slash path for internal jars
377
String JavaDoc rootPath = path.getDevice() == null ? path.toString() : path.toOSString();
378     String JavaDoc documentPath = rootPath + IJavaSearchScope.JAR_FILE_ENTRY_SEPARATOR + filePath;
379     IBinaryType binaryType = (IBinaryType)this.binariesFromIndexMatches.get(documentPath);
380     if (binaryType != null) {
381         this.infoToHandle.put(binaryType, classFile);
382         return binaryType;
383     } else {
384         return super.createInfoFromClassFileInJar(classFile);
385     }
386 }
387 /**
388  * Returns all of the possible subtypes of this type hierarchy.
389  * Returns null if they could not be determine.
390  */

391 private String JavaDoc[] determinePossibleSubTypes(final HashSet localTypes, IProgressMonitor monitor) {
392
393     class PathCollector implements IPathRequestor {
394         HashSet paths = new HashSet(10);
395         public void acceptPath(String JavaDoc path, boolean containsLocalTypes) {
396             this.paths.add(path);
397             if (containsLocalTypes) {
398                 localTypes.add(path);
399             }
400         }
401     }
402     PathCollector collector = new PathCollector();
403     
404     try {
405         if (monitor != null) monitor.beginTask("", MAXTICKS); //$NON-NLS-1$
406
searchAllPossibleSubTypes(
407             this.getType(),
408             this.scope,
409             this.binariesFromIndexMatches,
410             collector,
411             IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH,
412             monitor);
413     } finally {
414         if (monitor != null) monitor.done();
415     }
416
417     HashSet paths = collector.paths;
418     int length = paths.size();
419     String JavaDoc[] result = new String JavaDoc[length];
420     int count = 0;
421     for (Iterator iter = paths.iterator(); iter.hasNext();) {
422         result[count++] = (String JavaDoc) iter.next();
423     }
424     return result;
425 }
426
427 /**
428  * Find the set of candidate subtypes of a given type.
429  *
430  * The requestor is notified of super type references (with actual path of
431  * its occurrence) for all types which are potentially involved inside a particular
432  * hierarchy.
433  * The match locator is not used here to narrow down the results, the type hierarchy
434  * resolver is rather used to compute the whole hierarchy at once.
435  * @param type
436  * @param scope
437  * @param binariesFromIndexMatches
438  * @param pathRequestor
439  * @param waitingPolicy
440  * @param progressMonitor
441  */

442 public static void searchAllPossibleSubTypes(
443     IType type,
444     IJavaSearchScope scope,
445     final Map binariesFromIndexMatches,
446     final IPathRequestor pathRequestor,
447     int waitingPolicy, // WaitUntilReadyToSearch | ForceImmediateSearch | CancelIfNotReadyToSearch
448
final IProgressMonitor progressMonitor) {
449
450     /* embed constructs inside arrays so as to pass them to (inner) collector */
451     final Queue queue = new Queue();
452     final HashtableOfObject foundSuperNames = new HashtableOfObject(5);
453
454     IndexManager indexManager = JavaModelManager.getJavaModelManager().getIndexManager();
455
456     /* use a special collector to collect paths and queue new subtype names */
457     IndexQueryRequestor searchRequestor = new IndexQueryRequestor() {
458         public boolean acceptIndexMatch(String JavaDoc documentPath, SearchPattern indexRecord, SearchParticipant participant, AccessRuleSet access) {
459             SuperTypeReferencePattern record = (SuperTypeReferencePattern)indexRecord;
460             boolean isLocalOrAnonymous = record.enclosingTypeName == IIndexConstants.ONE_ZERO;
461             pathRequestor.acceptPath(documentPath, isLocalOrAnonymous);
462             char[] typeName = record.simpleName;
463             int suffix = documentPath.toLowerCase().lastIndexOf(SUFFIX_STRING_class);
464             if (suffix != -1){
465                 HierarchyBinaryType binaryType = (HierarchyBinaryType)binariesFromIndexMatches.get(documentPath);
466                 if (binaryType == null){
467                     char[] enclosingTypeName = record.enclosingTypeName;
468                     if (isLocalOrAnonymous) {
469                         int lastSlash = documentPath.lastIndexOf('/');
470                         int lastDollar = documentPath.lastIndexOf('$');
471                         if (lastDollar == -1) {
472                             // malformed local or anonymous type: it doesn't contain a $ in its name
473
// treat it as a top level type
474
enclosingTypeName = null;
475                             typeName = documentPath.substring(lastSlash+1, suffix).toCharArray();
476                         } else {
477                             enclosingTypeName = documentPath.substring(lastSlash+1, lastDollar).toCharArray();
478                             typeName = documentPath.substring(lastDollar+1, suffix).toCharArray();
479                         }
480                     }
481                     binaryType = new HierarchyBinaryType(record.modifiers, record.pkgName, typeName, enclosingTypeName, record.typeParameterSignatures, record.classOrInterface);
482                     binariesFromIndexMatches.put(documentPath, binaryType);
483                 }
484                 binaryType.recordSuperType(record.superSimpleName, record.superQualification, record.superClassOrInterface);
485             }
486             if (!isLocalOrAnonymous // local or anonymous types cannot have subtypes outside the cu that define them
487
&& !foundSuperNames.containsKey(typeName)){
488                 foundSuperNames.put(typeName, typeName);
489                 queue.add(typeName);
490             }
491             return true;
492         }
493     };
494
495     int superRefKind;
496     try {
497         superRefKind = type.isClass() ? SuperTypeReferencePattern.ONLY_SUPER_CLASSES : SuperTypeReferencePattern.ALL_SUPER_TYPES;
498     } catch (JavaModelException e) {
499         superRefKind = SuperTypeReferencePattern.ALL_SUPER_TYPES;
500     }
501     SuperTypeReferencePattern pattern =
502         new SuperTypeReferencePattern(null, null, superRefKind, SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE);
503     MatchLocator.setFocus(pattern, type);
504     SubTypeSearchJob job = new SubTypeSearchJob(
505         pattern,
506         new JavaSearchParticipant(), // java search only
507
scope,
508         searchRequestor);
509
510     int ticks = 0;
511     queue.add(type.getElementName().toCharArray());
512     try {
513         while (queue.start <= queue.end) {
514             if (progressMonitor != null && progressMonitor.isCanceled()) return;
515
516             // all subclasses of OBJECT are actually all types
517
char[] currentTypeName = queue.retrieve();
518             if (CharOperation.equals(currentTypeName, IIndexConstants.OBJECT))
519                 currentTypeName = null;
520
521             // search all index references to a given supertype
522
pattern.superSimpleName = currentTypeName;
523             indexManager.performConcurrentJob(job, waitingPolicy, progressMonitor == null ? null : new NullProgressMonitor() {
524                 // don't report progress since this is too costly for deep hierarchies
525
// just handle isCanceled() (seehttps://bugs.eclipse.org/bugs/show_bug.cgi?id=179511)
526
public void setCanceled(boolean value) {
527                     progressMonitor.setCanceled(value);
528                 }
529                 public boolean isCanceled() {
530                     return progressMonitor.isCanceled();
531                 }
532             });
533             if (progressMonitor != null && ++ticks <= MAXTICKS)
534                 progressMonitor.worked(1);
535
536             // in case, we search all subtypes, no need to search further
537
if (currentTypeName == null) break;
538         }
539     } finally {
540         job.finished();
541     }
542 }
543 }
544
Popular Tags