Index: editor/src/org/netbeans/modules/java/editor/imports/ComputeImports.java =================================================================== RCS file: /cvs/java/editor/src/org/netbeans/modules/java/editor/imports/ComputeImports.java,v --- editor/src/org/netbeans/modules/java/editor/imports/ComputeImports.java 22 Mar 2007 09:36:52 -0000 1.6 +++ editor/src/org/netbeans/modules/java/editor/imports/ComputeImports.java 13 Jun 2007 12:45:49 -0000 @@ -105,12 +105,16 @@ return new Pair(Collections.emptyMap(), Collections.emptyMap()); List classes = new ArrayList(); - - for (ElementHandle typeNames : info.getJavaSource().getClasspathInfo().getClassIndex().getDeclaredTypes(unresolved, NameKind.SIMPLE_NAME,EnumSet.allOf(ClassIndex.SearchScope.class))) { - TypeElement te = info.getElements().getTypeElement(typeNames.getQualifiedName()); + Set> typeNames = info.getJavaSource().getClasspathInfo().getClassIndex().getDeclaredTypes(unresolved, NameKind.SIMPLE_NAME,EnumSet.allOf(ClassIndex.SearchScope.class)); + if (typeNames == null) { + //Canceled + return new Pair(Collections.emptyMap(), Collections.emptyMap()); + } + for (ElementHandle typeName : typeNames) { + TypeElement te = info.getElements().getTypeElement(typeName.getQualifiedName()); if (te == null) { - Logger.getLogger(ComputeImports.class.getName()).log(Level.INFO, "Cannot resolve type element \"" + typeNames + "\"."); + Logger.getLogger(ComputeImports.class.getName()).log(Level.INFO, "Cannot resolve type element \"" + typeName + "\"."); continue; } Index: editor/src/org/netbeans/modules/java/editor/overridden/IsOverriddenAnnotationHandler.java =================================================================== RCS file: /cvs/java/editor/src/org/netbeans/modules/java/editor/overridden/IsOverriddenAnnotationHandler.java,v --- editor/src/org/netbeans/modules/java/editor/overridden/IsOverriddenAnnotationHandler.java 31 May 2007 10:57:49 -0000 1.7 +++ editor/src/org/netbeans/modules/java/editor/overridden/IsOverriddenAnnotationHandler.java 13 Jun 2007 12:45:49 -0000 @@ -440,8 +440,11 @@ ElementHandle eh = l.remove(0); result.add(eh); - - l.addAll(cpinfo.getClassIndex().getElements(eh, Collections.singleton(SearchKind.IMPLEMENTORS), EnumSet.of(ClassIndex.SearchScope.SOURCE))); + Set> typeElements = cpinfo.getClassIndex().getElements(eh, Collections.singleton(SearchKind.IMPLEMENTORS), EnumSet.of(ClassIndex.SearchScope.SOURCE)); + //XXX: Canceling + if (typeElements != null) { + l.addAll(typeElements); + } } return result; } finally { Index: editor/src/org/netbeans/modules/java/editor/overridden/IsOverriddenVisitor.java =================================================================== RCS file: /cvs/java/editor/src/org/netbeans/modules/java/editor/overridden/IsOverriddenVisitor.java,v --- editor/src/org/netbeans/modules/java/editor/overridden/IsOverriddenVisitor.java 17 Jan 2007 13:12:53 -0000 1.3 +++ editor/src/org/netbeans/modules/java/editor/overridden/IsOverriddenVisitor.java 13 Jun 2007 12:45:49 -0000 @@ -43,7 +43,6 @@ class IsOverriddenVisitor extends CancellableTreePathScanner { private CompilationInfo info; - private ClassIndex uq; private Document doc; Map, List>> type2Declaration; @@ -55,7 +54,6 @@ IsOverriddenVisitor(Document doc, CompilationInfo info) { this.doc = doc; this.info = info; - this.uq = info.getJavaSource().getClasspathInfo().getClassIndex(); type2Declaration = new HashMap, List>>(); declaration2Tree = new HashMap, MethodTree>(); Index: source/apichanges.xml =================================================================== RCS file: /cvs/java/source/apichanges.xml,v --- source/apichanges.xml 5 Jun 2007 13:08:54 -0000 1.10 +++ source/apichanges.xml 13 Jun 2007 12:46:02 -0000 @@ -83,6 +83,20 @@ + + + ClassIndex methods are cancellable + + + + + + The ClassIndex methods called from the CancellableTask are cancellable. In case when they are cancelled by the JavaSource infrastructure + they return null instead of the Set to allow client to determine this state. The incompatible semantic change is that tasks registered by + factories have to check if the value returned from class index is null. + + + Added a method to obtain top level elements defined in the source/class file. Index: source/nbproject/project.properties =================================================================== RCS file: /cvs/java/source/nbproject/project.properties,v --- source/nbproject/project.properties 7 Jun 2007 08:39:10 -0000 1.21 +++ source/nbproject/project.properties 13 Jun 2007 12:46:03 -0000 @@ -21,5 +21,5 @@ javadoc.title=Java Source javadoc.arch=${basedir}/arch.xml javadoc.apichanges=${basedir}/apichanges.xml -spec.version.base=0.14.0 +spec.version.base=0.15.0 test.unit.run.cp.extra=${core.dir}/core/core.jar:${core.dir}/lib/boot.jar:../../junit/external/insanelib.jar Index: source/src/org/netbeans/api/java/source/ClassIndex.java =================================================================== RCS file: /cvs/java/source/src/org/netbeans/api/java/source/ClassIndex.java,v --- source/src/org/netbeans/api/java/source/ClassIndex.java 8 Jun 2007 08:25:58 -0000 1.7 +++ source/src/org/netbeans/api/java/source/ClassIndex.java 13 Jun 2007 12:46:03 -0000 @@ -213,7 +213,8 @@ * @param searchKind type of reference, {@see SearchKind} * @param scope to search in {@see SearchScope} * @return set of {@link ElementHandle}s containing the reference(s) - * + * It may return null when the caller is a CancellableTask<CompilationInfo> and is cancelled + * inside call of this method. */ public Set> getElements (final ElementHandle element, final Set searchKind, final Set scope) { assert element != null; @@ -224,12 +225,16 @@ final Set ut = encodeSearchKind(element.getKind(),searchKind); final String binaryName = element.getSignature()[0]; final ResultConvertor> thConvertor = ResultConvertor.elementHandleConvertor(); - if (!ut.isEmpty()) { - for (ClassIndexImpl query : queries) { - query.search(binaryName, ut, thConvertor, result); + try { + if (!ut.isEmpty()) { + for (ClassIndexImpl query : queries) { + query.search(binaryName, ut, thConvertor, result); + } } + return Collections.unmodifiableSet(result); + } catch (InterruptedException e) { + return null; } - return Collections.unmodifiableSet(result); } /** @@ -238,7 +243,8 @@ * @param searchKind type of reference, {@see SearchKind} * @param scope to search in {@see SearchScope} * @return set of {@link FileObject}s containing the reference(s) - * + * It may return null when the caller is a CancellableTask<CompilationInfo> and is cancelled + * inside call of this method. */ public Set getResources (final ElementHandle element, final Set searchKind, final Set scope) { assert element != null; @@ -248,13 +254,17 @@ final Iterable queries = this.getQueries (scope); final Set ut = encodeSearchKind(element.getKind(),searchKind); final String binaryName = element.getSignature()[0]; - if (!ut.isEmpty()) { - for (ClassIndexImpl query : queries) { - final ResultConvertor foConvertor = ResultConvertor.fileObjectConvertor (query.getSourceRoots()); - query.search (binaryName, ut, foConvertor, result); + try { + if (!ut.isEmpty()) { + for (ClassIndexImpl query : queries) { + final ResultConvertor foConvertor = ResultConvertor.fileObjectConvertor (query.getSourceRoots()); + query.search (binaryName, ut, foConvertor, result); + } } + return Collections.unmodifiableSet(result); + } catch (InterruptedException e) { + return null; } - return Collections.unmodifiableSet(result); } @@ -265,6 +275,8 @@ * @param kind of the name {@see NameKind} * @param scope to search in {@see SearchScope} * @return set of all matched declared types + * It may return null when the caller is a CancellableTask<CompilationInfo> and is cancelled + * inside call of this method. */ public Set> getDeclaredTypes (final String name, final NameKind kind, final Set scope) { assert name != null; @@ -272,11 +284,15 @@ final Set> result = new HashSet>(); final Iterable queries = this.getQueries (scope); final ResultConvertor> thConvertor = ResultConvertor.elementHandleConvertor(); - for (ClassIndexImpl query : queries) { - query.getDeclaredTypes (name, kind, thConvertor, result); + try { + for (ClassIndexImpl query : queries) { + query.getDeclaredTypes (name, kind, thConvertor, result); + } + LOGGER.fine(String.format("ClassIndex.getDeclaredTypes returned %d elements\n", result.size())); + return Collections.unmodifiableSet(result); + } catch (InterruptedException e) { + return null; } - LOGGER.fine(String.format("ClassIndex.getDeclaredTypes returned %d elements\n", result.size())); - return Collections.unmodifiableSet(result); } /** @@ -286,15 +302,21 @@ * the nearest component of the package. * @param scope to search in {@see SearchScope} * @return set of all matched package names + * It may return null when the caller is a CancellableTask<CompilationInfo> and is cancelled + * inside call of this method. */ public Set getPackageNames (final String prefix, boolean directOnly, final Set scope) { assert prefix != null; final Set result = new HashSet (); final Iterable queries = this.getQueries (scope); - for (ClassIndexImpl query : queries) { - query.getPackageNames (prefix, directOnly, result); + try { + for (ClassIndexImpl query : queries) { + query.getPackageNames (prefix, directOnly, result); + } + return Collections.unmodifiableSet(result); + } catch (InterruptedException e) { + return null; } - return Collections.unmodifiableSet(result); } // Private innerclasses ---------------------------------------------------- Index: source/src/org/netbeans/api/java/source/JavaSource.java =================================================================== RCS file: /cvs/java/source/src/org/netbeans/api/java/source/JavaSource.java,v --- source/src/org/netbeans/api/java/source/JavaSource.java 5 Jun 2007 13:08:55 -0000 1.60 +++ source/src/org/netbeans/api/java/source/JavaSource.java 13 Jun 2007 12:46:07 -0000 @@ -111,6 +111,7 @@ import org.netbeans.modules.java.source.tasklist.CompilerSettings; import org.netbeans.modules.java.source.usages.ClassIndexImpl; import org.netbeans.modules.java.source.usages.ClassIndexManager; +import org.netbeans.modules.java.source.usages.Index; import org.netbeans.modules.java.source.usages.RepositoryUpdater; import org.netbeans.modules.java.source.util.LowMemoryEvent; import org.netbeans.modules.java.source.util.LowMemoryListener; @@ -1436,7 +1437,12 @@ //The state (or greater) was reached and document was not modified during moveToPhase try { final long startTime = System.currentTimeMillis(); - ((CancellableTask)r.task).run (ci); //XXX: How to do it in save way? + Index.cancel.set(currentRequest.getCanceledRef()); + try { + ((CancellableTask)r.task).run (ci); //XXX: How to do it in save way? + } finally { + Index.cancel.remove(); + } final long endTime = System.currentTimeMillis(); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest(String.format("executed task: %s in %d ms.", //NOI18N @@ -1822,10 +1828,11 @@ private JavaSource.Request reference; private JavaSource.Request canceledReference; private long cancelTime; - private boolean canceled; + private final AtomicBoolean canceled; private boolean mayCancelJavac; CurrentRequestReference () { + this.canceled = new AtomicBoolean(); } boolean setCurrentTask (JavaSource.Request reference) throws InterruptedException { @@ -1834,8 +1841,8 @@ while (this.canceledReference!=null) { INTERNAL_LOCK.wait(); } - result = this.canceled; - this.canceled = this.mayCancelJavac = false; + result = this.canceled.getAndSet(false); + this.mayCancelJavac = false; this.cancelTime = 0; this.reference = reference; } @@ -1851,7 +1858,7 @@ request = this.reference; this.canceledReference = request; this.reference = null; - this.canceled = true; + this.canceled.set(true); this.cancelTime = System.currentTimeMillis(); } } @@ -1868,7 +1875,7 @@ request = this.reference; this.canceledReference = request; this.reference = null; - this.canceled = true; + this.canceled.set(true); this.mayCancelJavac = mayCancelJavac; this.cancelTime = System.currentTimeMillis(); } @@ -1892,7 +1899,7 @@ request = this.reference; this.canceledReference = request; this.reference = null; - this.canceled = true; + this.canceled.set(true); } } } @@ -1908,7 +1915,7 @@ assert this.canceledReference == null; this.canceledReference = request; this.reference = null; - this.canceled = true; + this.canceled.set(true); this.cancelTime = System.currentTimeMillis(); } } @@ -1940,7 +1947,7 @@ if (!result) { this.reference = null; } - this.canceled = result; + this.canceled.set(result); this.cancelTime = System.currentTimeMillis(); } } @@ -1950,8 +1957,12 @@ boolean isCanceled () { synchronized (INTERNAL_LOCK) { - return this.canceled; + return this.canceled.get(); } + } + + AtomicBoolean getCanceledRef () { + return this.canceled; } boolean isInterruptJavac () { Index: source/src/org/netbeans/modules/java/source/JBrowseModule.java =================================================================== RCS file: /cvs/java/source/src/org/netbeans/modules/java/source/JBrowseModule.java,v --- source/src/org/netbeans/modules/java/source/JBrowseModule.java 22 Mar 2007 19:55:25 -0000 1.8 +++ source/src/org/netbeans/modules/java/source/JBrowseModule.java 13 Jun 2007 12:46:08 -0000 @@ -79,7 +79,10 @@ }); } catch (IOException ex) { Exceptions.printStackTrace(ex); - }; + } + catch (InterruptedException e) { + Exceptions.printStackTrace(e); + } if (ENABLE_MBEANS) { unregisterMBeans(); } Index: source/src/org/netbeans/modules/java/source/usages/ClassIndexImpl.java =================================================================== RCS file: /cvs/java/source/src/org/netbeans/modules/java/source/usages/ClassIndexImpl.java,v --- source/src/org/netbeans/modules/java/source/usages/ClassIndexImpl.java 11 Apr 2007 11:45:51 -0000 1.4 +++ source/src/org/netbeans/modules/java/source/usages/ClassIndexImpl.java 13 Jun 2007 12:46:09 -0000 @@ -27,9 +27,7 @@ import java.util.List; import java.util.Set; import org.netbeans.api.java.source.ClassIndex; -import org.netbeans.api.java.source.ClassIndexListener; import org.netbeans.api.java.source.JavaSource; -import org.netbeans.api.java.source.TypesEvent; import org.openide.filesystems.FileObject; import org.openide.util.Utilities; @@ -64,11 +62,11 @@ public static ClassIndexFactory FACTORY; - public abstract void search (final String binaryName, final Set usageType, final ResultConvertor convertor, final Set result); + public abstract void search (final String binaryName, final Set usageType, final ResultConvertor convertor, final Set result) throws InterruptedException; - public abstract void getDeclaredTypes (String name, ClassIndex.NameKind kind, final ResultConvertor convertor, final Set result); + public abstract void getDeclaredTypes (String name, ClassIndex.NameKind kind, final ResultConvertor convertor, final Set result) throws InterruptedException; - public abstract void getPackageNames (String prefix, boolean directOnly, Set result); + public abstract void getPackageNames (String prefix, boolean directOnly, Set result) throws InterruptedException; public abstract FileObject[] getSourceRoots (); Index: source/src/org/netbeans/modules/java/source/usages/ClassIndexManager.java =================================================================== RCS file: /cvs/java/source/src/org/netbeans/modules/java/source/usages/ClassIndexManager.java,v --- source/src/org/netbeans/modules/java/source/usages/ClassIndexManager.java 11 Apr 2007 11:45:52 -0000 1.4 +++ source/src/org/netbeans/modules/java/source/usages/ClassIndexManager.java 13 Jun 2007 12:46:09 -0000 @@ -65,7 +65,7 @@ this.listeners.remove(listener); } - public T writeLock (final ExceptionAction r) throws IOException { + public T writeLock (final ExceptionAction r) throws IOException, InterruptedException { this.lock.writeLock().lock(); try { depth++; @@ -94,7 +94,7 @@ } } - public T readLock (final ExceptionAction r) throws IOException { + public T readLock (final ExceptionAction r) throws IOException, InterruptedException { this.lock.readLock().lock(); try { return r.run(); @@ -149,7 +149,7 @@ } public static interface ExceptionAction { - public T run () throws IOException; + public T run () throws IOException, InterruptedException; } private void fire (final Set roots, final byte op) { Index: source/src/org/netbeans/modules/java/source/usages/Index.java =================================================================== RCS file: /cvs/java/source/src/org/netbeans/modules/java/source/usages/Index.java,v --- source/src/org/netbeans/modules/java/source/usages/Index.java 16 Apr 2007 13:57:01 -0000 1.12 +++ source/src/org/netbeans/modules/java/source/usages/Index.java 13 Jun 2007 12:46:09 -0000 @@ -31,6 +31,7 @@ import java.util.Map; import java.util.Properties; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import org.netbeans.api.java.source.ClassIndex; import org.openide.ErrorManager; import org.openide.filesystems.FileUtil; @@ -60,12 +61,16 @@ private static File segmentsFile; private static int index = 0; - public abstract boolean isValid (boolean tryOpen) throws IOException; - public abstract List getUsagesData (String resourceName, Set mask, BooleanOperator operator) throws IOException; - public abstract List getUsagesFQN (String resourceName, Set mask, BooleanOperator operator) throws IOException; - public abstract List getReferencesData (String resourceName) throws IOException; - public abstract void getDeclaredTypes (String simpleName, ClassIndex.NameKind kind, ResultConvertor convertor, Set result) throws IOException; - public abstract void getPackageNames (String prefix, boolean directOnly, Set result) throws IOException; + public static final ThreadLocal cancel = new ThreadLocal () { + protected synchronized AtomicBoolean initialValue() { + return new AtomicBoolean (); + } + }; + + public abstract boolean isValid (boolean tryOpen) throws IOException; + public abstract List getUsagesFQN (String resourceName, Set mask, BooleanOperator operator) throws IOException, InterruptedException; + public abstract void getDeclaredTypes (String simpleName, ClassIndex.NameKind kind, ResultConvertor convertor, Set result) throws IOException, InterruptedException; + public abstract void getPackageNames (String prefix, boolean directOnly, Set result) throws IOException, InterruptedException; public abstract void store (Map> refs, Set toDelete) throws IOException; public abstract void store (Map> refs, List topLevels) throws IOException; public abstract boolean isUpToDate (String resourceName, long timeStamp) throws IOException; Index: source/src/org/netbeans/modules/java/source/usages/LuceneIndex.java =================================================================== RCS file: /cvs/java/source/src/org/netbeans/modules/java/source/usages/LuceneIndex.java,v --- source/src/org/netbeans/modules/java/source/usages/LuceneIndex.java 11 Apr 2007 16:09:25 -0000 1.14 +++ source/src/org/netbeans/modules/java/source/usages/LuceneIndex.java 13 Jun 2007 12:46:10 -0000 @@ -21,6 +21,7 @@ import java.io.File; import java.io.IOException; +import java.io.InterruptedIOException; import java.text.ParseException; import java.util.Comparator; import java.util.EnumSet; @@ -80,7 +81,7 @@ private IndexReader reader; //Cache, do not use this dirrectly, use getReader private Set rootPkgCache; //Cache, do not use this dirrectly - public static Index create (final File cacheRoot) throws IOException { + static Index create (final File cacheRoot) throws IOException { assert cacheRoot != null && cacheRoot.exists() && cacheRoot.canRead() && cacheRoot.canWrite(); return new LuceneIndex (getReferencesCacheFolder(cacheRoot)); } @@ -92,10 +93,10 @@ } @SuppressWarnings("unchecked") // NOI18N, unchecked - lucene has source 1.4 - public List getUsagesData(final String resourceName, Set mask, BooleanOperator operator) throws IOException { + private List getUsagesData(final String resourceName, Set mask, BooleanOperator operator) throws IOException { if (!isValid(false)) { return null; - } + } final Searcher searcher = new IndexSearcher (this.getReader()); try { final List result = new LinkedList (); @@ -139,10 +140,12 @@ } @SuppressWarnings ("unchecked") // NOI18N, unchecked - lucene has source 1.4 - public List getUsagesFQN(final String resourceName, final Setmask, final BooleanOperator operator) throws IOException { + public List getUsagesFQN(final String resourceName, final Setmask, final BooleanOperator operator) throws IOException, InterruptedException { if (!isValid(false)) { return null; } + final AtomicBoolean cancel = this.cancel.get(); + assert cancel != null; assert resourceName != null; assert mask != null; assert operator != null; @@ -164,9 +167,15 @@ break; default: throw new IllegalArgumentException (operator.toString()); - } + } + if (cancel.get()) { + throw new InterruptedException (); + } final Hits hits = searcher.search (query); for (Iterator it = (Iterator) hits.iterator(); it.hasNext();) { + if (cancel.get()) { + throw new InterruptedIOException (); + } final Hit hit = it.next (); final Document doc = hit.getDocument(); final String user = DocumentUtil.getBinaryName(doc); @@ -178,7 +187,7 @@ } } - public List getReferencesData(final String resourceName) throws IOException { + private List getReferencesData(final String resourceName) throws IOException { if (!isValid(false)) { return null; } @@ -200,11 +209,13 @@ } @SuppressWarnings ("unchecked") // NOI18N, unchecked - lucene has source 1.4 - public void getDeclaredTypes (final String name, final ClassIndex.NameKind kind, final ResultConvertor convertor, final Set result) throws IOException { + public void getDeclaredTypes (final String name, final ClassIndex.NameKind kind, final ResultConvertor convertor, final Set result) throws IOException, InterruptedException { if (!isValid(false)) { LOGGER.fine(String.format("LuceneIndex[%s] is invalid!\n", this.toString())); return; } + final AtomicBoolean cancel = this.cancel.get(); + assert cancel != null; assert name != null; final Set toSearch = new TreeSet (new Comparator(){ public int compare (Term t1, Term t2) { @@ -225,23 +236,23 @@ case PREFIX: if (name.length() == 0) { //Special case (all) handle in different way - emptyPrefixSearch(in, convertor, result); + emptyPrefixSearch(in, convertor, result, cancel); return; } else { final Term nameTerm = DocumentUtil.simpleNameTerm(name); - prefixSearh(nameTerm, in, toSearch); + prefixSearh(nameTerm, in, toSearch, cancel); break; } case CASE_INSENSITIVE_PREFIX: if (name.length() == 0) { //Special case (all) handle in different way - emptyPrefixSearch(in, convertor, result); + emptyPrefixSearch(in, convertor, result, cancel); return; } else { final Term nameTerm = DocumentUtil.caseInsensitiveNameTerm(name.toLowerCase()); //XXX: I18N, Locale - prefixSearh(nameTerm, in, toSearch); + prefixSearh(nameTerm, in, toSearch, cancel); break; } case CAMEL_CASE: @@ -266,7 +277,7 @@ } } final Pattern pattern = Pattern.compile(patternString.toString()); - regExpSearch(pattern,DocumentUtil.simpleNameTerm(Character.toString(startChar)),in,toSearch); + regExpSearch(pattern,DocumentUtil.simpleNameTerm(Character.toString(startChar)),in,toSearch,cancel); break; } case CASE_INSENSITIVE_REGEXP: @@ -275,7 +286,7 @@ } { final Pattern pattern = Pattern.compile(name,Pattern.CASE_INSENSITIVE); - regExpSearch(pattern, DocumentUtil.caseInsensitiveNameTerm(name.toLowerCase()), in, toSearch); //XXX: Locale + regExpSearch(pattern, DocumentUtil.caseInsensitiveNameTerm(name.toLowerCase()), in, toSearch,cancel); //XXX: Locale break; } case REGEXP: @@ -284,7 +295,7 @@ } { final Pattern pattern = Pattern.compile(name); - regExpSearch(pattern, DocumentUtil.simpleNameTerm(name), in, toSearch); + regExpSearch(pattern, DocumentUtil.simpleNameTerm(name), in, toSearch, cancel); break; } default: @@ -296,19 +307,25 @@ final ElementKind[] kindHolder = new ElementKind[1]; Set docNums = new TreeSet(); while (it.hasNext()) { + if (cancel.get()) { + throw new InterruptedException (); + } tds.seek(it.next()); while (tds.next()) { docNums.add (tds.doc()); } } for (Integer docNum : docNums) { + if (cancel.get()) { + throw new InterruptedException (); + } final Document doc = in.document(docNum); final String binaryName = DocumentUtil.getBinaryName(doc, kindHolder); result.add (convertor.convert(kindHolder[0],binaryName)); } } - private void regExpSearch (final Pattern pattern, final Term startTerm, final IndexReader in, final Set toSearch) throws IOException { + private void regExpSearch (final Pattern pattern, final Term startTerm, final IndexReader in, final Set toSearch, final AtomicBoolean cancel) throws IOException, InterruptedException { final String startText = startTerm.text(); final StringBuilder startBuilder = new StringBuilder (); startBuilder.append(startText.charAt(0)); @@ -324,6 +341,9 @@ final TermEnum en = in.terms(startTerm); try { do { + if (cancel.get()) { + throw new InterruptedException (); + } Term term = en.term(); if (term != null && camelField == term.field() && term.text().startsWith(startPrefix)) { final Matcher m = pattern.matcher(term.text()); @@ -340,10 +360,13 @@ } } - private void emptyPrefixSearch (final IndexReader in, final ResultConvertor convertor, final Set result) throws IOException { + private void emptyPrefixSearch (final IndexReader in, final ResultConvertor convertor, final Set result, final AtomicBoolean cancel) throws IOException, InterruptedException { final int bound = in.maxDoc(); final ElementKind[] kindHolder = new ElementKind[1]; for (int i=0; i toSearch) throws IOException { + private void prefixSearh (Term nameTerm, final IndexReader in, final Set toSearch, final AtomicBoolean cancel) throws IOException, InterruptedException { final String prefixField = nameTerm.field(); final String name = nameTerm.text(); final TermEnum en = in.terms(nameTerm); try { do { + if (cancel.get()) { + throw new InterruptedException (); + } Term term = en.term(); if (term != null && prefixField == term.field() && term.text().startsWith(name)) { toSearch.add (term); @@ -380,10 +406,12 @@ } - public void getPackageNames (final String prefix, final boolean directOnly, final Set result) throws IOException { + public void getPackageNames (final String prefix, final boolean directOnly, final Set result) throws IOException, InterruptedException { if (!isValid(false)) { return; - } + } + final AtomicBoolean cancel = this.cancel.get(); + assert cancel != null; final IndexReader in = getReader(); final Term pkgTerm = DocumentUtil.packageNameTerm (prefix); final String prefixField = pkgTerm.field(); @@ -398,6 +426,9 @@ final TermEnum terms = in.terms (); try { do { + if (cancel.get()) { + throw new InterruptedIOException (); + } final Term currentTerm = terms.term(); if (currentTerm != null && prefixField == currentTerm.field()) { String pkgName = currentTerm.text(); @@ -420,6 +451,9 @@ final TermEnum terms = in.terms (pkgTerm); try { do { + if (cancel.get()) { + throw new InterruptedException (); + } final Term currentTerm = terms.term(); if (currentTerm != null && prefixField == currentTerm.field() && currentTerm.text().startsWith(prefix)) { String pkgName = currentTerm.text(); Index: source/src/org/netbeans/modules/java/source/usages/PersistentClassIndex.java =================================================================== RCS file: /cvs/java/source/src/org/netbeans/modules/java/source/usages/PersistentClassIndex.java,v --- source/src/org/netbeans/modules/java/source/usages/PersistentClassIndex.java 16 Nov 2006 09:56:31 -0000 1.4 +++ source/src/org/netbeans/modules/java/source/usages/PersistentClassIndex.java 13 Jun 2007 12:46:11 -0000 @@ -53,13 +53,14 @@ private final boolean isSource; private WeakReference dirty; private static final Logger LOGGER = Logger.getLogger(PersistentClassIndex.class.getName()); + private static IndexFactory indexFactory; /** Creates a new instance of ClassesAndMembersUQ */ private PersistentClassIndex(final URL root, final File cacheRoot, final boolean source) throws IOException, IllegalArgumentException { assert root != null; this.root = root; - this.index = LuceneIndex.create (cacheRoot); + this.index = (indexFactory == null ? LuceneIndex.create (cacheRoot) : indexFactory.create(cacheRoot)); this.isSource = source; } @@ -92,7 +93,7 @@ } // Implementation of UsagesQueryImpl --------------------------------------- - public void search (final String binaryName, final Set usageType, final ResultConvertor convertor, final Set result) { + public void search (final String binaryName, final Set usageType, final ResultConvertor convertor, final Set result) throws InterruptedException { updateDirty(); if (BinaryAnalyser.OBJECT.equals(binaryName)) { this.getDeclaredTypes("", ClassIndex.NameKind.PREFIX, convertor, result); @@ -100,7 +101,7 @@ } try { ClassIndexManager.getDefault().readLock(new ClassIndexManager.ExceptionAction () { - public Void run () throws IOException { + public Void run () throws IOException, InterruptedException { usages(binaryName, usageType, convertor, result); return null; } @@ -113,11 +114,11 @@ - public void getDeclaredTypes (final String simpleName, final ClassIndex.NameKind kind, final ResultConvertor convertor, final Set result) { + public void getDeclaredTypes (final String simpleName, final ClassIndex.NameKind kind, final ResultConvertor convertor, final Set result) throws InterruptedException { updateDirty(); try { ClassIndexManager.getDefault().readLock(new ClassIndexManager.ExceptionAction () { - public Void run () throws IOException { + public Void run () throws IOException, InterruptedException { index.getDeclaredTypes (simpleName,kind, convertor, result); return null; } @@ -128,10 +129,10 @@ } - public void getPackageNames (final String prefix, final boolean directOnly, final Set result) { + public void getPackageNames (final String prefix, final boolean directOnly, final Set result) throws InterruptedException { try { ClassIndexManager.getDefault().readLock(new ClassIndexManager.ExceptionAction() { - public Void run () throws IOException { + public Void run () throws IOException, InterruptedException { index.getPackageNames(prefix, directOnly, result); return null; } @@ -154,6 +155,11 @@ return "CompromiseUQ["+this.root.toExternalForm()+"]"; // NOI18N } + //Unit test methods + public static void setIndexFactory (final IndexFactory factory) { + indexFactory = factory; + } + //Protected methods -------------------------------------------------------- protected final void close () throws IOException { this.index.close(); @@ -191,6 +197,10 @@ } catch (IOException ioe) { Exceptions.printStackTrace(ioe); } + catch (InterruptedException e) { + //Should never happen + Exceptions.printStackTrace(e); + } } else { try { @@ -211,6 +221,10 @@ } catch (IOException ioe) { Exceptions.printStackTrace(ioe); } + catch (InterruptedException e) { + //Should never happen + Exceptions.printStackTrace(e); + } } public void cancel () {} @@ -228,7 +242,7 @@ } } - private void usages (final String binaryName, final Set usageType, ResultConvertor convertor, Set result) { + private void usages (final String binaryName, final Set usageType, ResultConvertor convertor, Set result) throws InterruptedException { final List classInternalNames = this.getUsagesFQN(binaryName,usageType, Index.BooleanOperator.OR); for (String classInternalName : classInternalNames) { T value = convertor.convert(ElementKind.OTHER, classInternalName); @@ -238,7 +252,7 @@ } } - private List getUsagesFQN (final String binaryName, final Set mask, final Index.BooleanOperator operator) { + private List getUsagesFQN (final String binaryName, final Set mask, final Index.BooleanOperator operator) throws InterruptedException { List result = null; try { result = this.index.getUsagesFQN(binaryName, mask, operator); Index: source/src/org/netbeans/modules/java/source/usages/RepositoryUpdater.java =================================================================== RCS file: /cvs/java/source/src/org/netbeans/modules/java/source/usages/RepositoryUpdater.java,v --- source/src/org/netbeans/modules/java/source/usages/RepositoryUpdater.java 31 May 2007 12:14:50 -0000 1.56 +++ source/src/org/netbeans/modules/java/source/usages/RepositoryUpdater.java 13 Jun 2007 12:46:16 -0000 @@ -889,6 +889,7 @@ } public void run (final CompilationInfo nullInfo) throws IOException { + try { ClassIndexManager.getDefault().writeLock (new ClassIndexManager.ExceptionAction () { @SuppressWarnings("fallthrough") @@ -1129,6 +1130,10 @@ } } }}); + } catch (InterruptedException e) { + //Never thrown + Exceptions.printStackTrace(e); + } } private void findDependencies (final URL rootURL, final Stack cycleDetector, final Map> depGraph, Index: source/test/unit/src/org/netbeans/api/java/source/JavaSourceTest.java =================================================================== RCS file: /cvs/java/source/test/unit/src/org/netbeans/api/java/source/JavaSourceTest.java,v --- source/test/unit/src/org/netbeans/api/java/source/JavaSourceTest.java 21 May 2007 08:35:56 -0000 1.13 +++ source/test/unit/src/org/netbeans/api/java/source/JavaSourceTest.java 13 Jun 2007 12:46:19 -0000 @@ -36,6 +36,7 @@ import java.net.URI; import java.net.URL; import java.text.MessageFormat; +import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; @@ -53,12 +54,19 @@ import java.util.EnumSet; import java.util.LinkedList; import java.util.List; +import java.util.Set; import java.util.concurrent.TimeUnit; import javax.swing.text.BadLocationException; import org.netbeans.api.java.classpath.ClassPath; +import org.netbeans.api.java.source.ClassIndex; +import org.netbeans.api.java.source.ClassIndex.NameKind; import org.netbeans.junit.NbTestCase; import org.netbeans.junit.NbTestSuite; import org.netbeans.modules.java.source.parsing.SourceFileObject; +import org.netbeans.modules.java.source.usages.ClassIndexImpl.UsageType; +import org.netbeans.modules.java.source.usages.Index; +import org.netbeans.modules.java.source.usages.Index; +import org.netbeans.modules.java.source.usages.ResultConvertor; import org.netbeans.spi.java.classpath.support.ClassPathSupport; import org.openide.cookies.EditorCookie; import org.openide.cookies.SaveCookie; @@ -74,7 +82,9 @@ import org.netbeans.modules.java.preprocessorbridge.spi.JavaFileFilterImplementation; import org.netbeans.modules.java.source.JavaSourceAccessor; import org.netbeans.modules.java.source.classpath.CacheClassPath; +import org.netbeans.modules.java.source.usages.IndexFactory; import org.netbeans.modules.java.source.usages.IndexUtil; +import org.netbeans.modules.java.source.usages.PersistentClassIndex; import org.netbeans.modules.java.source.usages.RepositoryUpdater; import org.netbeans.spi.java.classpath.ClassPathProvider; /** @@ -1280,6 +1290,86 @@ }, true); } + + public void testIndexCancel() throws Exception { + PersistentClassIndex.setIndexFactory(new TestIndexFactory()); + try { + FileObject test = createTestFile ("Test1"); + final ClassPath bootPath = createBootPath (); + final ClassPath compilePath = createCompilePath (); + final ClassPath sourcePath = createSourcePath (); + + + ClassLoader l = JavaSourceTest.class.getClassLoader(); + Lkp.DEFAULT.setLookupsWrapper( + Lookups.metaInfServices(l), + Lookups.singleton(l), + Lookups.singleton(new ClassPathProvider() { + public ClassPath findClassPath(FileObject file, String type) { + if (ClassPath.BOOT == type) { + return bootPath; + } + + if (ClassPath.SOURCE == type) { + return sourcePath; + } + + if (ClassPath.COMPILE == type) { + return compilePath; + } + return null; + } + })); + + + JavaSource js = JavaSource.create(ClasspathInfo.create(bootPath, compilePath, sourcePath), test); + CountDownLatch rl = RepositoryUpdater.getDefault().scheduleCompilationAndWait(sourcePath.getRoots()[0], sourcePath.getRoots()[0]); + rl.await(); + DataObject dobj = DataObject.find(test); + EditorCookie ec = (EditorCookie) dobj.getCookie(EditorCookie.class); + final StyledDocument doc = ec.openDocument(); + Thread.sleep(500); //It may happen that the js is invalidated before the dispatch of task is done and the test of timers may fail + + final CountDownLatch[] end = new CountDownLatch[]{new CountDownLatch (1)}; + final Object[] result = new Object[1]; + + CancellableTask task = new CancellableTask() { + + public void cancel() { + } + + public void run(CompilationInfo p) throws Exception { + ClassIndex index = p.getClasspathInfo().getClassIndex(); + result[0] = index.getPackageNames("javax", true, EnumSet.allOf(ClassIndex.SearchScope.class)); + end[0].countDown(); + } + + }; + js.addPhaseCompletionTask (task,Phase.PARSED, Priority.HIGH); + Thread.sleep(500); //Making test a more deterministic, when the task is cancelled by DocListener, it's hard for test to recover from it + NbDocument.runAtomic (doc, + new Runnable () { + public void run () { + try { + String text = doc.getText(0,doc.getLength()); + int index = text.indexOf(REPLACE_PATTERN); + assertTrue (index != -1); + doc.remove(index,REPLACE_PATTERN.length()); + doc.insertString(index,"System.out.println();",null); + } catch (BadLocationException ble) { + ble.printStackTrace(System.out); + } + } + }); + end[0].await(); + assertNull(result[0]); + js.removePhaseCompletionTask (task); + } finally { + PersistentClassIndex.setIndexFactory(null); + } + } + + private static class TestProvider implements JavaSource.JavaFileObjectProvider { private Object lock; @@ -1597,6 +1687,65 @@ } + private static class TestIndexFactory implements IndexFactory { + + public Index create(File cacheRoot) throws IOException { + return new TestIndex (); + } + + } + + private static class TestIndex extends Index { + + + public TestIndex () { + } + + public boolean isValid(boolean tryOpen) throws IOException { + return true; + } + + public List getUsagesFQN(String resourceName, Set mask, BooleanOperator operator) throws IOException, InterruptedException { + await (); + return Collections.emptyList(); + } + + public void getDeclaredTypes(String simpleName, NameKind kind, ResultConvertor convertor, Set result) throws IOException, InterruptedException { + await (); + } + + public void getPackageNames(String prefix, boolean directOnly, Set result) throws IOException, InterruptedException { + await (); + } + + public void store(Map> refs, Set toDelete) throws IOException { + } + + public void store(Map> refs, List topLevels) throws IOException { + } + + public boolean isUpToDate(String resourceName, long timeStamp) throws IOException { + return true; + } + + public void clear() throws IOException { + } + + public void close() throws IOException { + } + + private void await () throws InterruptedException { + AtomicBoolean cancel = this.cancel.get(); + while (true) { + if (cancel.get()) { + throw new InterruptedException (); + } + Thread.sleep(100); + } + } + + } + private FileObject createTestFile (String className) { try { File workdir = this.getWorkDir(); @@ -1636,6 +1785,15 @@ private ClassPath createCompilePath () { return ClassPathSupport.createClassPath(Collections.EMPTY_LIST); + } + + private ClassPath createSourcePath () throws IOException { + File workdir = this.getWorkDir(); + File root = new File (workdir, "src"); + if (!root.exists()) { + root.mkdirs(); + } + return ClassPathSupport.createClassPath(new URL[] {root.toURI().toURL()}); } private FileObject createTestFile (FileObject srcRoot, String relativeName, String content) throws IOException {