--- a/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionDoc.java +++ a/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionDoc.java @@ -45,9 +45,9 @@ package org.netbeans.modules.editor.java; import java.net.URL; +import java.util.concurrent.Callable; import javax.lang.model.element.Element; import javax.swing.Action; -import com.sun.javadoc.*; import org.netbeans.api.java.source.CompilationController; import org.netbeans.api.java.source.ui.ElementJavadoc; import org.netbeans.spi.editor.completion.CompletionDocumentation; @@ -81,8 +81,8 @@ return elementJavadoc.getGotoSourceAction(); } - public static final JavaCompletionDoc create(CompilationController controller, Element element) { - return new JavaCompletionDoc( ElementJavadoc.create(controller, element) ); + public static final JavaCompletionDoc create(CompilationController controller, Element element, Callable callable) { + return new JavaCompletionDoc( ElementJavadoc.create(controller, element, callable) ); } } --- a/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionProvider.java +++ a/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionProvider.java @@ -49,6 +49,7 @@ import java.io.IOException; import java.util.*; +import java.util.concurrent.Callable; import java.util.concurrent.Future; import java.util.logging.Logger; import java.util.logging.Level; @@ -550,7 +551,11 @@ case ENUM_CONSTANT: case FIELD: case METHOD: - documentation = JavaCompletionDoc.create(controller, el); + documentation = JavaCompletionDoc.create(controller, el, new Callable() { + public Boolean call() { + return isTaskCancelled(); + } + }); } } } --- a/java.source/src/org/netbeans/modules/java/source/JavadocHelper.java +++ a/java.source/src/org/netbeans/modules/java/source/JavadocHelper.java @@ -61,6 +61,10 @@ import java.util.Set; import java.util.StringTokenizer; import java.util.WeakHashMap; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.logging.Level; import java.util.logging.Logger; import javax.lang.model.element.Element; @@ -81,6 +85,7 @@ import org.openide.filesystems.FileStateInvalidException; import org.openide.filesystems.FileUtil; import org.openide.filesystems.URLMapper; +import org.openide.util.RequestProcessor; /** * Utilities to assist with retrieval of Javadoc text. @@ -88,6 +93,7 @@ public class JavadocHelper { private static final Logger LOG = Logger.getLogger(JavadocHelper.class.getName()); + private static final RequestProcessor RP = new RequestProcessor(JavadocHelper.class.getName(),1); private JavadocHelper() {} @@ -185,16 +191,17 @@ } private static final Map cachedJavadoc = new WeakHashMap(); - + /** * Richer version of {@link SourceUtils#getJavadoc}. - * Finds {@link URL} of a javadoc page for given element when available. This method + * Finds {@link URL} of a javadoc page for given element when available. This method * uses {@link JavadocForBinaryQuery} to find the javadoc page for the give element. * For {@link PackageElement} it returns the package-summary.html for given package. * @param element to find the Javadoc for + * @param cancel a Callable to signal cancel request * @return the javadoc page or null when the javadoc is not available. */ - public static TextStream getJavadoc(Element element) { + public static TextStream getJavadoc(Element element, final Callable cancel) { synchronized (cachedJavadoc) { TextStream result = cachedJavadoc.get(element); if (result != null) { @@ -202,19 +209,22 @@ return result; } } - TextStream result = doGetJavadoc(element); + TextStream result = doGetJavadoc(element, cancel); synchronized (cachedJavadoc) { cachedJavadoc.put(element, result); } return result; } + public static TextStream getJavadoc(Element element) { + return getJavadoc(element, null); + } + @org.netbeans.api.annotations.common.SuppressWarnings(value="DMI_COLLECTION_OF_URLS"/*,justification="URLs have never host part"*/) - private static TextStream doGetJavadoc(final Element element) { + private static TextStream doGetJavadoc(final Element element, final Callable cancel) { if (element == null) { throw new IllegalArgumentException("Cannot pass null as an argument of the SourceUtils.getJavadoc"); // NOI18N } - ClassSymbol clsSym = null; String pkgName; String pageName; @@ -258,25 +268,59 @@ if (clsSym.completer != null) { clsSym.complete(); } + if (clsSym.classfile != null) { + try { + final URL classFile = clsSym.classfile.toUri().toURL(); + final String pkgNameF = pkgName; + final String pageNameF = pageName; + final CharSequence fragment = buildFragment ? getFragment(element) : null; + final Future future = RP.submit(new Callable() { + @Override + public TextStream call() throws Exception { + return findJavadoc(classFile, pkgNameF, pageNameF, fragment); + } + }); + do { + if (cancel != null && cancel.call()) { + break; + } + try { + return future.get(100, TimeUnit.MILLISECONDS); + } catch (TimeoutException timeOut) { + //Retry + } + } while (true); + } catch (Exception e) { + LOG.log(Level.INFO, null, e); + } + } + return null; + } + + private static final String PACKAGE_SUMMARY = "package-summary"; // NOI18N + + private static TextStream findJavadoc( + final URL classFile, + final String pkgName, + final String pageName, + final CharSequence fragment) { URL sourceRoot = null; Set binaries = new HashSet(); try { - if (clsSym.classfile != null) { - FileObject fo = URLMapper.findFileObject(clsSym.classfile.toUri().toURL()); - StringTokenizer tk = new StringTokenizer(pkgName, "/"); // NOI18N - for (int i = 0; fo != null && i <= tk.countTokens(); i++) { - fo = fo.getParent(); - } - if (fo != null) { - URL url = fo.getURL(); - sourceRoot = JavaIndex.getSourceRootForClassFolder(url); - if (sourceRoot == null) { - binaries.add(url); - } else { - // sourceRoot may be a class root in reality - binaries.add(sourceRoot); - } + FileObject fo = URLMapper.findFileObject(classFile); + StringTokenizer tk = new StringTokenizer(pkgName, "/"); // NOI18N + for (int i = 0; fo != null && i <= tk.countTokens(); i++) { + fo = fo.getParent(); + } + if (fo != null) { + URL url = fo.getURL(); + sourceRoot = JavaIndex.getSourceRootForClassFolder(url); + if (sourceRoot == null) { + binaries.add(url); + } else { + // sourceRoot may be a class root in reality + binaries.add(sourceRoot); } } if (sourceRoot != null) { @@ -303,8 +347,8 @@ out: for (URL e : roots) { FileObject[] res = SourceForBinaryQuery.findSourceRoots(e).getRoots(); - for (FileObject fo : res) { - if (sourceRoots.contains(fo)) { + for (FileObject r : res) { + if (sourceRoots.contains(r)) { binaries.add(e); continue out; } @@ -313,7 +357,7 @@ } } } - CharSequence fragment = buildFragment ? getFragment(element) : null; + for (URL binary : binaries) { JavadocForBinaryQuery.Result javadocResult = JavadocForBinaryQuery.findJavadoc(binary); URL[] result = javadocResult.getRoots(); @@ -366,8 +410,6 @@ } return null; } - - private static final String PACKAGE_SUMMARY = "package-summary"; // NOI18N /** * {@code ElementJavadoc} currently will check every class in an API set if you keep on using code completion. --- a/java.sourceui/apichanges.xml +++ a/java.sourceui/apichanges.xml @@ -110,6 +110,20 @@ + + + Adding cancelable version of ElementJavadoc.create + + + + + + + Added a cancelable version of ElementJavadoc.create to allow + code completion to cancel slow call to JavadocForBinaryQuery. + + + Adding ElementOpen.open(ClasspathInfo info, ElementHandle h) --- a/java.sourceui/nbproject/project.properties +++ a/java.sourceui/nbproject/project.properties @@ -2,4 +2,4 @@ javac.compilerargs=-Xlint -Xlint:-serial javac.source=1.6 javadoc.arch=${basedir}/arch.xml -spec.version.base=1.14.0 +spec.version.base=1.15.0 --- a/java.sourceui/src/org/netbeans/api/java/source/ui/ElementJavadoc.java +++ a/java.sourceui/src/org/netbeans/api/java/source/ui/ElementJavadoc.java @@ -53,6 +53,7 @@ import com.sun.source.util.Trees; import java.net.MalformedURLException; import java.util.ArrayList; +import java.util.concurrent.Callable; import org.netbeans.api.java.source.ClasspathInfo; import org.netbeans.api.java.source.CompilationController; import org.netbeans.api.java.source.CompilationInfo; @@ -121,7 +122,21 @@ * @return ElementJavadoc describing the javadoc */ public static final ElementJavadoc create(CompilationInfo compilationInfo, Element element) { - return new ElementJavadoc(compilationInfo, element, null); + return create (compilationInfo, element, null); + } + + /** Creates an object describing the Javadoc of given element. The object + * is capable of getting the text formated into HTML, resolve the links, + * jump to external javadoc. + * + * @param compilationInfo CompilationInfo + * @param element Element the javadoc is required for + * @param cancel a {@link Callable} to signal the cancel request + * @return ElementJavadoc describing the javadoc + * @since 1.15 + */ + public static final ElementJavadoc create(CompilationInfo compilationInfo, Element element, final Callable cancel) { + return new ElementJavadoc(compilationInfo, element, null, cancel); } /** Gets the javadoc comment formated as HTML. @@ -167,7 +182,7 @@ public void run(CompilationController controller) throws IOException { controller.toPhase(Phase.ELEMENTS_RESOLVED); if (linkDoc != null) { - ret[0] = new ElementJavadoc(controller, linkDoc.resolve(controller), null); + ret[0] = new ElementJavadoc(controller, linkDoc.resolve(controller), null, null); } else { int idx = link.indexOf('#'); //NOI18N URI uri = null; @@ -197,7 +212,7 @@ } } } - ret[0] = new ElementJavadoc(controller, e, new URL(docURL, link)); + ret[0] = new ElementJavadoc(controller, e, new URL(docURL, link), null); } else { //external URL if( uri.isAbsolute() ) { @@ -230,7 +245,7 @@ return goToSource; } - private ElementJavadoc(CompilationInfo compilationInfo, Element element, URL url) { + private ElementJavadoc(CompilationInfo compilationInfo, Element element, URL url, final Callable cancel) { this.trees = compilationInfo.getTrees(); this.eu = compilationInfo.getElementUtilities(); this.cpInfo = compilationInfo.getClasspathInfo(); @@ -240,7 +255,7 @@ JavadocHelper.TextStream page = null; if (element != null) { // XXX would be better to avoid testing network connections in case we get a source fo anyway - page = JavadocHelper.getJavadoc(element); + page = JavadocHelper.getJavadoc(element, cancel); if (page != null) { docURL = page.getLocation(); }