KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > corext > util > AllTypesCache


1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 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 org.eclipse.jdt.internal.corext.util;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.Arrays JavaDoc;
15 import java.util.Collection JavaDoc;
16 import java.util.Comparator JavaDoc;
17 import java.util.HashSet JavaDoc;
18 import java.util.Set JavaDoc;
19
20 import org.eclipse.core.runtime.IProgressMonitor;
21 import org.eclipse.core.runtime.NullProgressMonitor;
22 import org.eclipse.core.runtime.OperationCanceledException;
23 import org.eclipse.core.runtime.Platform;
24 import org.eclipse.core.runtime.SubProgressMonitor;
25
26 import org.eclipse.core.resources.ResourcesPlugin;
27
28 import org.eclipse.swt.widgets.Display;
29
30 import org.eclipse.jdt.core.ElementChangedEvent;
31 import org.eclipse.jdt.core.ICompilationUnit;
32 import org.eclipse.jdt.core.IElementChangedListener;
33 import org.eclipse.jdt.core.IJavaElement;
34 import org.eclipse.jdt.core.IJavaElementDelta;
35 import org.eclipse.jdt.core.JavaCore;
36 import org.eclipse.jdt.core.JavaModelException;
37 import org.eclipse.jdt.core.search.IJavaSearchConstants;
38 import org.eclipse.jdt.core.search.IJavaSearchScope;
39 import org.eclipse.jdt.core.search.ITypeNameRequestor;
40 import org.eclipse.jdt.core.search.SearchEngine;
41 import org.eclipse.jdt.core.search.SearchPattern;
42
43 import org.eclipse.jdt.internal.corext.CorextMessages;
44
45 import org.eclipse.jdt.internal.ui.JavaPlugin;
46
47 /**
48  * Manages a search cache for types in the workspace. Instead of returning objects of type <code>IType</code>
49  * the methods of this class returns a list of the lightweight objects <code>TypeInfo</code>.
50  * <P>
51  * The AllTypesCache supports a synchronous and an asynchronous (background) mode.
52  * In synchronous mode a dirty cache is rebuild as soon as <code>getAllTypes()</code> is called directly (or indirectly).
53  * In asynchronous mode dirtying the cache starts a timer thread which rebuilds the cache after TIMEOUT seconds.
54  * If the cache becomes dirty again while the background query is running, the query is aborted and the timer reset.
55  * If <code>getAllTypes</code> is called before the background job has finished, getAllTypes waits
56  * for the termination of the background query.
57  * <P>
58  * AllTypesCache automatically switches from synchronous to asynchronous mode when it detects
59  * that the UI event loop is running.
60  */

61 public class AllTypesCache {
62     
63     private static final int INITIAL_DELAY= 4000;
64     private static final int TIMEOUT= 3000;
65     private static final int INITIAL_SIZE= 2000;
66     private static final int CANCEL_POLL_INTERVAL= 300;
67     
68     static class DelegatedProgressMonitor implements IProgressMonitor {
69         private IProgressMonitor fDelegate;
70         private String JavaDoc fTaskName;
71 // private String fSubTask;
72
private int fTotalWork= IProgressMonitor.UNKNOWN;
73         private double fWorked;
74         
75         public synchronized void beginTask(String JavaDoc name, int totalWork) {
76             fTaskName= name;
77             fTotalWork= totalWork;
78             if (fDelegate != null)
79                 fDelegate.beginTask(name, totalWork);
80         }
81         public void done() {
82         }
83         public synchronized void internalWorked(double work) {
84             fWorked+= work;
85             if (fDelegate != null)
86                 fDelegate.internalWorked(work);
87         }
88         public boolean isCanceled() {
89             return false;
90         }
91         public void setCanceled(boolean value) {
92         }
93         public synchronized void setTaskName(String JavaDoc name) {
94             fTaskName= name;
95             if (fDelegate != null)
96                 fDelegate.setTaskName(name);
97         }
98         public synchronized void subTask(String JavaDoc name) {
99 // fSubTask= name;
100
if (fDelegate != null)
101                 fDelegate.subTask(name);
102         }
103         public void worked(int work) {
104             internalWorked(work);
105         }
106         synchronized void setDelegate(IProgressMonitor delegate) {
107             fDelegate= delegate;
108             if (fDelegate != null) {
109                 if (fTaskName != null) {
110                     fDelegate.beginTask(fTaskName, fTotalWork);
111                     fDelegate.internalWorked(fWorked);
112                 }
113             } else {
114                 fTaskName= null;
115                 fTotalWork= IProgressMonitor.UNKNOWN;
116                 fWorked= 0.0;
117             }
118         }
119     }
120
121     /* debugging flag to enable tracing */
122     private static final boolean TRACING;
123     static {
124         String JavaDoc value= Platform.getDebugOption("org.eclipse.jdt.ui/debug/allTypesCache"); //$NON-NLS-1$
125
TRACING= value != null && value.equalsIgnoreCase("true"); //$NON-NLS-1$
126
}
127     
128     static class TypeCacher extends Thread JavaDoc {
129         
130         private int fDelay;
131         private int fSizeHint;
132         private volatile boolean fRestart;
133         private volatile boolean fAbort;
134         
135         TypeCacher(int sizeHint, int delay) {
136             super("All Types Caching"); //$NON-NLS-1$
137
fSizeHint= sizeHint;
138             fDelay= delay;
139             setPriority(Thread.NORM_PRIORITY - 1);
140         }
141         
142         void restart() {
143             fRestart= true;
144             interrupt();
145         }
146         
147         public void abort() {
148             fRestart= fAbort= true;
149             interrupt();
150         }
151         
152         public void run() {
153             // we loop until we got a complete result
154
while (!fAbort) {
155                 if (fDelay > 0) {
156                     try {
157                         Thread.sleep(fDelay);
158                     } catch (InterruptedException JavaDoc e) {
159                         if (fAbort)
160                             break;
161                         continue; // restart timer
162
}
163                 }
164                 
165                 fRestart= false;
166                 Collection JavaDoc searchResult= doSearchTypes();
167                 if (searchResult != null) {
168                     if (!fAbort && !fRestart) {
169                         TypeInfo[] result= (TypeInfo[]) searchResult.toArray(new TypeInfo[searchResult.size()]);
170                         Arrays.sort(result, fgTypeNameComparator);
171                         setCache(result);
172                     }
173                     if (!fRestart)
174                         break;
175                 }
176             }
177         }
178         
179         private Collection JavaDoc doSearchTypes() {
180             
181             if (ResourcesPlugin.getWorkspace() == null)
182                 return null;
183             
184             final ArrayList JavaDoc typesFound= new ArrayList JavaDoc(fSizeHint);
185
186             class RequestorAbort extends RuntimeException JavaDoc { }
187         
188             ITypeNameRequestor requestor= new TypeInfoRequestor(typesFound) {
189                 protected boolean inScope(char[] packageName) {
190                     if (fRestart)
191                         throw new RequestorAbort();
192                     return super.inScope(packageName);
193                 }
194             };
195
196             try {
197                 if (! search(requestor, IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, null))
198                     return null;
199             } catch (RequestorAbort e) {
200                 // query cancelled
201
return null;
202             }
203             return typesFound;
204         }
205     }
206     
207     /**
208      * The lock for synchronizing all activity in the AllTypesCache.
209      */

210     private static Object JavaDoc fgLock= new Object JavaDoc();
211         private volatile static boolean fgIsLocked;
212         private volatile static TypeCacher fgTypeCacherThread;
213         private static TypeInfo[] fgTypeCache;
214         private static int fgSizeHint= INITIAL_SIZE;
215         private volatile static boolean fgTerminated;
216         private volatile static boolean fgAsyncMode; // if true cache updates are run via a background thread
217

218     
219     private static int fgNumberOfCacheFlushes;
220     private static DelegatedProgressMonitor fDelegatedProgressMonitor= new DelegatedProgressMonitor();
221     private static TypeCacheDeltaListener fgDeltaListener;
222
223     /**
224      * A comparator for simple type names
225      */

226     private static Comparator JavaDoc fgTypeNameComparator= new Comparator JavaDoc() {
227         public int compare(Object JavaDoc o1, Object JavaDoc o2) {
228             return ((TypeInfo)o1).getTypeName().compareTo(((TypeInfo)o2).getTypeName());
229         }
230     };
231         
232     /**
233      * Returns all types in the given scope.
234      * @param kind IJavaSearchConstants.CLASS, IJavaSearchConstants.INTERFACE
235      * or IJavaSearchConstants.TYPE
236      * @param typesFound The resulting <code>TypeInfo</code> elements are added to this collection
237      */

238     public static void getTypes(IJavaSearchScope scope, int kind, IProgressMonitor monitor, Collection JavaDoc typesFound) {
239         
240         TypeInfo[] allTypes= getAllTypes(monitor);
241         
242         boolean isWorkspaceScope= scope.equals(SearchEngine.createWorkspaceScope());
243         boolean isBoth= (kind == IJavaSearchConstants.TYPE);
244         boolean isInterface= (kind == IJavaSearchConstants.INTERFACE);
245         
246         for (int i= 0; i < allTypes.length; i++) {
247             TypeInfo info= fgTypeCache[i];
248             if (isWorkspaceScope || info.isEnclosed(scope)) {
249                 if (isBoth || (isInterface == info.isInterface())) {
250                     typesFound.add(info);
251                 }
252             }
253         }
254     }
255     
256     private static void setCache(TypeInfo[] cache) {
257         synchronized(fgLock) {
258             fgTypeCache= cache;
259             if (fgTypeCache != null)
260                 fgSizeHint= fgTypeCache.length;
261             fgTypeCacherThread= null;
262             fgLock.notifyAll();
263         }
264     }
265
266     /**
267      * Returns all types in the workspace. The returned array must not be
268      * modified. The elements in the array are sorted by simple type name.
269      */

270     public static TypeInfo[] getAllTypes(IProgressMonitor monitor) {
271         if (monitor == null)
272             monitor= new NullProgressMonitor();
273         monitor.beginTask("", 10); //$NON-NLS-1$
274
SubProgressMonitor checkingMonitor = new SubProgressMonitor(monitor, 1);
275         SubProgressMonitor searchingMonitor= new SubProgressMonitor(monitor, 9);
276         forceDeltaComplete(checkingMonitor);
277         searchingMonitor.setTaskName(CorextMessages.getString("AllTypesCache.searching")); //$NON-NLS-1$
278

279         synchronized(fgLock) {
280             try {
281                 if (fgTypeCache == null) {
282                     // cache is empty
283

284                     if (fgTypeCacherThread == null) {
285                         // cache synchroneously
286
ArrayList JavaDoc searchResult= new ArrayList JavaDoc(fgSizeHint);
287                         try {
288                             fgIsLocked= true;
289                             if (search(new TypeInfoRequestor(searchResult), IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, searchingMonitor)) {
290                                 TypeInfo[] result= (TypeInfo[]) searchResult.toArray(new TypeInfo[searchResult.size()]);
291                                 Arrays.sort(result, fgTypeNameComparator);
292                                 fgTypeCache= result;
293                                 fgSizeHint= result.length;
294                             }
295                         } finally {
296                             fgIsLocked= false;
297                         }
298                     } else {
299                         fDelegatedProgressMonitor.setDelegate(searchingMonitor);
300                         // wait for the thread to finished
301
try {
302                             while (fgTypeCache == null) {
303                                 fgLock.wait(CANCEL_POLL_INTERVAL); // poll for cancel
304
if (searchingMonitor.isCanceled())
305                                     throw new OperationCanceledException();
306                             }
307                         } catch (InterruptedException JavaDoc e) {
308                             JavaPlugin.log(e);
309                         } finally {
310                             fDelegatedProgressMonitor.setDelegate(null);
311                         }
312                     }
313                 }
314                 
315             } finally {
316                 monitor.done();
317             }
318             return fgTypeCache;
319         }
320     }
321         
322     /**
323      * Returns true if the type cache is up to date.
324      */

325     public static boolean isCacheUpToDate(IProgressMonitor pm) {
326         forceDeltaComplete(pm);
327         return fgTypeCache != null;
328     }
329     
330     private static void forceDeltaComplete(IProgressMonitor pm) {
331         if (pm == null)
332             pm= new NullProgressMonitor();
333
334         ICompilationUnit[] primaryWorkingCopies= JavaCore.getWorkingCopies(null);
335         pm.beginTask("", primaryWorkingCopies.length); //$NON-NLS-1$
336
pm.setTaskName(CorextMessages.getString("AllTypesCache.checking_type_cache")); //$NON-NLS-1$
337
for (int i= 0; i < primaryWorkingCopies.length; i++) {
338             ICompilationUnit curr= primaryWorkingCopies[i];
339             try {
340                 JavaModelUtil.reconcile(curr);
341             } catch (JavaModelException e) {
342                 JavaPlugin.log(e);
343             }
344             pm.worked(1);
345             if (pm.isCanceled())
346                 throw new OperationCanceledException();
347         }
348         pm.done();
349     }
350     
351     /**
352      * Returns a hint for the number of all types in the workspace.
353      */

354     public static int getNumberOfAllTypesHint() {
355         return fgSizeHint;
356     }
357     
358     
359     public static void forceCacheFlush() {
360         if (fgTerminated)
361             return;
362         if (TRACING) {
363             System.out.println("All Types Cache -- cache flushed."); //$NON-NLS-1$
364
}
365         
366         synchronized(fgLock) {
367             fgTypeCache= null;
368             fgNumberOfCacheFlushes++;
369             
370             if (fgTypeCacherThread != null) {
371                 // if caching thread is already running, restart it
372
fgTypeCacherThread.restart();
373             } else {
374                 if (fgAsyncMode) { // start thread only if we are in background mode
375
fgTypeCacherThread= new TypeCacher(fgSizeHint, TIMEOUT);
376                     fgTypeCacherThread.start();
377                 }
378             }
379         }
380     }
381     
382     
383     private static class TypeCacheDeltaListener implements IElementChangedListener {
384         
385         /*
386          * @see IElementChangedListener#elementChanged
387          */

388         public void elementChanged(ElementChangedEvent event) {
389             if (fgTerminated)
390                 return;
391             boolean needsFlushing= processDelta(event.getDelta());
392             if (needsFlushing) {
393                 forceCacheFlush();
394             }
395         }
396         
397         /*
398          * returns true if the cache needs to be flushed
399          */

400         private boolean processDelta(IJavaElementDelta delta) {
401             IJavaElement elem= delta.getElement();
402             boolean isAddedOrRemoved= (delta.getKind() != IJavaElementDelta.CHANGED)
403              || (delta.getFlags() & (IJavaElementDelta.F_ADDED_TO_CLASSPATH | IJavaElementDelta.F_REMOVED_FROM_CLASSPATH)) != 0;
404             
405             switch (elem.getElementType()) {
406                 case IJavaElement.JAVA_MODEL:
407                 case IJavaElement.JAVA_PROJECT:
408                 case IJavaElement.PACKAGE_FRAGMENT_ROOT:
409                 case IJavaElement.PACKAGE_FRAGMENT:
410                 case IJavaElement.CLASS_FILE:
411                 case IJavaElement.TYPE: // type children can be inner classes
412
if (isAddedOrRemoved) {
413                         return true;
414                     }
415                     return processChildrenDelta(delta);
416                 case IJavaElement.COMPILATION_UNIT: // content change means refresh from local
417
if (!JavaModelUtil.isPrimary((ICompilationUnit) elem)) {
418                         return false;
419                     }
420
421                     if (isAddedOrRemoved || isPossibleStructuralChange(delta.getFlags())) {
422                         return true;
423                     }
424                     return processChildrenDelta(delta);
425                 default:
426                     // fields, methods, imports ect
427
return false;
428             }
429         }
430         
431         private boolean isPossibleStructuralChange(int flags) {
432             return (flags & (IJavaElementDelta.F_CONTENT | IJavaElementDelta.F_FINE_GRAINED)) == IJavaElementDelta.F_CONTENT;
433         }
434         
435         private boolean processChildrenDelta(IJavaElementDelta delta) {
436             IJavaElementDelta[] children= delta.getAffectedChildren();
437             for (int i= 0; i < children.length; i++) {
438                 if (processDelta(children[i])) {
439                     return true;
440                 }
441             }
442             return false;
443         }
444     }
445     
446
447     /**
448      * Gets the number of times the cache was flushed. Used for testing.
449      * @return Returns a int
450      */

451     public static int getNumberOfCacheFlushes() {
452         return fgNumberOfCacheFlushes;
453     }
454
455     /**
456      * Returns all types for a given name in the scope. The elements in the array are sorted by simple type name.
457      */

458     public static TypeInfo[] getTypesForName(String JavaDoc simpleTypeName, IJavaSearchScope searchScope, IProgressMonitor monitor) {
459         Collection JavaDoc result= new ArrayList JavaDoc();
460         Set JavaDoc namesFound= new HashSet JavaDoc();
461         TypeInfo[] allTypes= AllTypesCache.getAllTypes(monitor); // all types in workspace, sorted by type name
462
TypeInfo key= new UnresolvableTypeInfo("", simpleTypeName, null, true, null); //$NON-NLS-1$
463
int index= Arrays.binarySearch(allTypes, key, fgTypeNameComparator);
464         if (index >= 0 && index < allTypes.length) {
465             for (int i= index - 1; i>= 0; i--) {
466                 TypeInfo curr= allTypes[i];
467                 if (simpleTypeName.equals(curr.getTypeName())) {
468                     if (!namesFound.contains(curr.getFullyQualifiedName()) && curr.isEnclosed(searchScope)) {
469                         result.add(curr);
470                         namesFound.add(curr.getFullyQualifiedName());
471                     }
472                 } else {
473                     break;
474                 }
475             }
476     
477             for (int i= index; i < allTypes.length; i++) {
478                 TypeInfo curr= allTypes[i];
479                 if (simpleTypeName.equals(curr.getTypeName())) {
480                     if (!namesFound.contains(curr.getFullyQualifiedName()) && curr.isEnclosed(searchScope)) {
481                         result.add(curr);
482                         namesFound.add(curr.getFullyQualifiedName());
483                     }
484                 } else {
485                     break;
486                 }
487             }
488         }
489         return (TypeInfo[]) result.toArray(new TypeInfo[result.size()]);
490     }
491     
492     /**
493      * Checks if the search index is up to date.
494      */

495     public static boolean isIndexUpToDate() {
496         class TypeFoundException extends RuntimeException JavaDoc {
497         }
498         ITypeNameRequestor requestor= new ITypeNameRequestor() {
499             public void acceptClass(char[] packageName, char[] simpleTypeName, char[][] enclosingTypeNames, String JavaDoc path) {
500                 throw new TypeFoundException();
501             }
502             public void acceptInterface(char[] packageName, char[] simpleTypeName, char[][] enclosingTypeNames, String JavaDoc path) {
503                 throw new TypeFoundException();
504             }
505         };
506         try {
507             if (! search(requestor, IJavaSearchConstants.CANCEL_IF_NOT_READY_TO_SEARCH, null))
508                 return false;
509         } catch (OperationCanceledException e) {
510             return false;
511         } catch (TypeFoundException e) {
512         }
513         return true;
514     }
515
516     /*
517      * A precanned type search.
518      * Returns false if a JavaModelException occured.
519      */

520     static boolean search(ITypeNameRequestor requestor, int waitingPolicy, IProgressMonitor monitor) {
521         long start= System.currentTimeMillis();
522         try {
523             if (monitor == null)
524                 monitor= fDelegatedProgressMonitor;
525         
526             new SearchEngine().searchAllTypeNames(
527                 null,
528                 null,
529                 SearchPattern.R_PATTERN_MATCH, // case insensitive
530
IJavaSearchConstants.TYPE,
531                 SearchEngine.createWorkspaceScope(),
532                 requestor,
533                 waitingPolicy,
534                 monitor);
535         } catch (JavaModelException e) {
536             JavaPlugin.log(e);
537             if (TRACING) {
538                 System.out.println("All Types Cache -- building cache failed"); //$NON-NLS-1$
539
}
540             return false;
541         }
542         if (TRACING) {
543             System.out.println("All Types Cache"); //$NON-NLS-1$
544
System.out.println("\t cache populated in thread: " + Thread.currentThread().getName()); //$NON-NLS-1$
545
System.out.println("\t time needed to perform search: " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$//$NON-NLS-2$)
546
}
547         return true;
548     }
549     
550     /**
551      * Initialize the AllTypesCache.
552      */

553     public static void initialize() {
554         
555         fgDeltaListener= new TypeCacheDeltaListener();
556         JavaCore.addElementChangedListener(fgDeltaListener);
557
558         Display d= Display.getDefault();
559         if (d != null)
560             d.asyncExec(
561                 new Runnable JavaDoc() {
562                     public void run() {
563                         startBackgroundMode();
564                     }
565                 }
566             );
567     }
568     
569     private static void startBackgroundMode() {
570         
571         long start= System.currentTimeMillis();
572         
573         // forceDeltaComplete(new NullProgressMonitor());
574

575         if (fgIsLocked) {
576             fgAsyncMode= true;
577             if (TRACING) {
578                 System.out.println("All Types Cache -- Background Mode started. Time needed " + (System.currentTimeMillis() - start) + "ms."); //$NON-NLS-1$//$NON-NLS-2$
579
}
580             return;
581         }
582         
583         synchronized(fgLock) {
584             if (fgTypeCacherThread != null) {
585                 // there is already a thread
586
if (fgTerminated) {
587                     if (fgTypeCacherThread != null)
588                         fgTypeCacherThread.abort();
589                 } else {
590                     // there is already a thread running
591
// do nothing
592
}
593             } else {
594                 if (fgTerminated) {
595                     // already terminated: do nothing
596
} else {
597                     fgAsyncMode= true;
598                     if (fgTypeCache != null) {
599                         // the cache is already up to date
600
} else {
601                         fgTypeCacherThread= new TypeCacher(fgSizeHint, INITIAL_DELAY);
602                         fgTypeCacherThread.start();
603                     }
604                 }
605             }
606         }
607         if (TRACING) {
608             System.out.println("All Types Cache -- Background Mode started. Time needed: " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$//$NON-NLS-2$
609
}
610     }
611     
612     /**
613      * Terminate the service provided by AllTypesCache.
614      */

615     public static void terminate() {
616         
617         if (fgDeltaListener != null)
618             JavaCore.removeElementChangedListener(fgDeltaListener);
619         fgDeltaListener= null;
620         
621         synchronized(fgLock) {
622             fgTerminated= true;
623             fgAsyncMode= false;
624             
625             if (fDelegatedProgressMonitor != null && !fDelegatedProgressMonitor.isCanceled())
626                 fDelegatedProgressMonitor.setCanceled(true);
627             
628             if (fgTypeCacherThread != null) {
629                 fgTypeCacherThread.abort();
630                 try {
631                     fgTypeCacherThread.join(1000);
632                 } catch (InterruptedException JavaDoc e) {
633                     JavaPlugin.log(e);
634                 }
635                 fgTypeCacherThread= null;
636             }
637         }
638     }
639 }
640
Popular Tags