blob: 9d06ecd890c455aa672c80067d4e7b0af67d2b9c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.api.java.source.ui;
import com.sun.source.doctree.AttributeTree;
import com.sun.source.doctree.DeprecatedTree;
import com.sun.source.doctree.DocCommentTree;
import com.sun.source.doctree.DocTree;
import com.sun.source.doctree.EndElementTree;
import com.sun.source.doctree.EntityTree;
import com.sun.source.doctree.InheritDocTree;
import com.sun.source.doctree.LinkTree;
import com.sun.source.doctree.LiteralTree;
import com.sun.source.doctree.ParamTree;
import com.sun.source.doctree.ReferenceTree;
import com.sun.source.doctree.ReturnTree;
import com.sun.source.doctree.SeeTree;
import com.sun.source.doctree.SinceTree;
import com.sun.source.doctree.StartElementTree;
import com.sun.source.doctree.TextTree;
import com.sun.source.doctree.ThrowsTree;
import com.sun.source.doctree.UnknownBlockTagTree;
import com.sun.source.doctree.ValueTree;
import com.sun.source.util.DocTreePath;
import com.sun.source.util.DocTreeScanner;
import com.sun.source.util.DocTrees;
import com.sun.source.util.TreePath;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.swing.AbstractAction;
import javax.swing.Action;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.ModuleElement;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.type.IntersectionType;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.type.WildcardType;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.swing.SwingUtilities;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.annotations.common.NullAllowed;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.java.queries.JavadocForBinaryQuery;
import org.netbeans.api.java.queries.SourceJavadocAttacher;
import org.netbeans.api.java.source.ClasspathInfo;
import org.netbeans.api.java.source.CompilationController;
import org.netbeans.api.java.source.CompilationInfo;
import org.netbeans.api.java.source.ElementHandle;
import org.netbeans.api.java.source.ElementUtilities;
import org.netbeans.api.java.source.JavaSource;
import org.netbeans.api.java.source.JavaSource.Phase;
import org.netbeans.api.java.source.SourceUtils;
import org.netbeans.modules.java.preprocessorbridge.api.JavaSourceUtil;
import org.netbeans.modules.java.source.JavadocHelper;
import org.netbeans.modules.java.source.parsing.FileObjects;
import org.netbeans.spi.java.classpath.support.ClassPathSupport;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileStateInvalidException;
import org.openide.filesystems.FileUtil;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
import org.openide.util.RequestProcessor;
import org.openide.xml.XMLUtil;
/** Utility class for viewing Javadoc comments as HTML.
*
* @author Dusan Balek, Petr Hrebejk, Tomas Zezula
*/
public class ElementJavadoc {
private static final String ASSOCIATE_JDOC = "associate-javadoc:"; //NOI18N
private static final String API = "/api"; //NOI18N
private static final Set<String> LANGS;
private static final RequestProcessor RP = new RequestProcessor(ElementJavadoc.class);
static {
Locale[] availableLocales = Locale.getAvailableLocales();
Set<String> locNames = new HashSet<>((int) (availableLocales.length/.75f) + 1);
for (Locale locale : availableLocales) {
locNames.add(locale.toString());
}
LANGS = Collections.unmodifiableSet(locNames);
}
private final ClasspathInfo cpInfo;
private final FileObject fileObject;
private final ElementHandle<? extends Element> handle;
//private Doc doc;
private volatile Future<String> content;
private final Callable<Boolean> cancel;
private Map<String, ElementHandle<? extends Element>> links = new HashMap<>();
private int linkCounter = 0;
private volatile URL docURL = null;
private volatile URL docRoot = null;
private volatile AbstractAction goToSource = null;
/** Non-normative notes about the API. Usually used for examples. */
private static final String APINOTE_TAG = "apiNote"; //NOI18N
/** Describes required behaviour of conforming implementations. Key is that the description is not inherited. */
private static final String IMPLSPEC_TAG = "implSpec"; //NOI18N
/** Non-normative notes about the implementation. Typically used for descriptions of the behaviour. Also not inherited. */
private static final String IMPLNOTE_TAG = "implNote"; //NOI18N
/** 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
* @return ElementJavadoc describing the javadoc
*/
public static final ElementJavadoc create(CompilationInfo compilationInfo, Element element) {
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<Boolean> cancel) {
return new ElementJavadoc(compilationInfo, element, null, cancel);
}
/** Gets the javadoc comment formated as HTML.
* @return HTML text of the javadoc
*/
public String getText() {
try {
final Future<String> tmp = content;
return tmp != null ? tmp.get() : null;
} catch (InterruptedException | ExecutionException ex) {
return null;
}
}
/** Gets the javadoc comment formated as HTML.
* @return {@link Future} of HTML text of the javadoc
* @since 1.20
*/
public Future<String> getTextAsync() {
return content;
}
/** Gets URL of the external javadoc.
* @return Text of the Javadoc comment formated as HTML
*/
public URL getURL() {
return docURL;
}
/** Resolves a link contained in the Javadoc comment to an object
* describing the linked javadoc
* @param link Link which has to be resolved
* @return ElementJavadoc describing the javadoc of liked element
*/
public ElementJavadoc resolveLink(final String link) {
if (link.startsWith(ASSOCIATE_JDOC)) {
final String root = link.substring(ASSOCIATE_JDOC.length());
try {
final CountDownLatch latch = new CountDownLatch(1);
final AtomicBoolean success = new AtomicBoolean();
SourceJavadocAttacher.attachJavadoc(
new URL(root),
new SourceJavadocAttacher.AttachmentListener() {
@Override
public void attachmentSucceeded() {
success.set(true);
latch.countDown();
}
@Override
public void attachmentFailed() {
latch.countDown();
}
});
if (!SwingUtilities.isEventDispatchThread()) {
latch.await();
return success.get() ?
resolveElement(handle, link):
new ElementJavadoc(NbBundle.getMessage(ElementJavadoc.class, "javadoc_attaching_failed"), cancel);
}
} catch (MalformedURLException | InterruptedException ex) {
Exceptions.printStackTrace(ex);
}
return null;
}
final ElementHandle<? extends Element> linkDoc = links.get(link);
return resolveElement(linkDoc, link);
}
@Override
public String toString() {
return String.format("ElementJavadoc[url=%s, handle=%s]", getURL(), handle); //NOI18N
}
private ElementJavadoc resolveElement(
final ElementHandle<?> handle,
final String link) {
final ElementJavadoc[] ret = new ElementJavadoc[1];
try {
FileObject fo = handle != null ? SourceUtils.getFile(handle, cpInfo) : null;
if (fo != null && fo.isFolder() && handle.getKind() == ElementKind.PACKAGE) {
fo = fo.getFileObject("package-info", "java"); //NOI18N
}
if (cpInfo == null && fo == null) {
//link cannot be resolved by this element
try {
URL u = getURL() != null ? new URL(getURL(), link) : new URL(link);
ret[0] = new ElementJavadoc(u, cancel);
} catch (MalformedURLException ex) {
// ignore
}
return ret[0];
}
JavaSource js = fo != null ? JavaSource.forFileObject(fo)
: fileObject != null ? JavaSource.forFileObject(fileObject)
: JavaSource.create(cpInfo);
if (js != null) {
js.runUserActionTask((controller) -> {
controller.toPhase(Phase.ELEMENTS_RESOLVED);
if (handle != null) {
ret[0] = new ElementJavadoc(controller, handle.resolve(controller), null, cancel);
} else {
int idx = link.indexOf('#'); //NOI18N
URI uri = null;
try {
uri = URI.create(idx < 0 ? link : link.substring(0, idx));
if (!uri.isAbsolute() && ElementJavadoc.this.handle != null) {
Element e = ElementJavadoc.this.handle.resolve(controller);
if (e != null) {
PackageElement pe = controller.getElements().getPackageOf(e);
uri = URI.create(FileObjects.resolveRelativePath(pe.getQualifiedName().toString(), uri.getPath()));
}
}
} catch (IllegalArgumentException iae) {}
if (uri != null) {
if (!uri.isAbsolute()) {
uri = uri.normalize();
}
String path = uri.toString();
int startIdx = path.lastIndexOf(".."); //NOI18N
startIdx = startIdx < 0 ? 0 : startIdx + 3;
int endIdx = path.lastIndexOf('.'); //NOI18N
if (endIdx >= 0)
path = path.substring(startIdx, endIdx);
String clsName = path.replace('/', '.'); //NOI18N
Element e = controller.getElements().getTypeElement(clsName);
if (e != null) {
if (idx >= 0) {
String fragment = link.substring(idx + 1);
idx = fragment.indexOf('('); //NOI18N
String name = idx < 0 ? fragment : fragment.substring(0, idx);
for (Element member : e.getEnclosedElements()) {
if (member.getSimpleName().contentEquals(name) && fragment.contentEquals(getFragment(member))) {
e = member;
break;
}
}
}
URL u;
if (uri.isAbsolute()) {
u = new URL(link);
} else if (getURL() != null) {
u = new URL(getURL(), link);
} else {
return;
}
ret[0] = new ElementJavadoc(controller, e, u, cancel);
} else {
//external URL
if( uri.isAbsolute() ) {
ret[0] = new ElementJavadoc( uri.toURL(), cancel );
} else if (getURL() != null) {
try {
ret[0] = new ElementJavadoc(new URL(getURL(), link), cancel);
} catch (MalformedURLException ex) {
// ignore
}
}
}
}
}
}, true);
}
} catch (IOException ioe) {
Exceptions.printStackTrace(ioe);
}
if (ret[0] != null) {
try {
while (cancel != null && !cancel.call()) {
try {
ret[0].getTextAsync().get(250, TimeUnit.MILLISECONDS);
break;
} catch (TimeoutException timeOut) {/*retry*/}
}
} catch (Exception ex) {}
}
return ret[0];
}
/** Gets action capable of jumping to source of the Element this Javadoc
* belongs to.
* @return Action going to the source of the Element described by this javadoc.
*/
public Action getGotoSourceAction() {
return goToSource;
}
private ElementJavadoc(CompilationInfo compilationInfo, Element element, final URL url, final Callable<Boolean> cancel) {
this.cpInfo = compilationInfo.getClasspathInfo();
this.fileObject = compilationInfo.getFileObject();
this.handle = element == null ? null : ElementHandle.create(element);
this.cancel = cancel;
final StringBuilder header = getElementHeader(element, compilationInfo);
try {
//Optimisitic no http
CharSequence doc = getElementDoc(element, compilationInfo, header, url, true);
if (doc == null) {
computeDocURL(Collections.emptyList(), true, cancel);
doc = header.append(noJavadocFound());
}
this.content = new Now(doc.toString());
} catch (JavadocHelper.RemoteJavadocException re) {
if (fileObject == null || JavaSource.forFileObject(fileObject) == null) {
header.append(noJavadocFound());
this.content = new Now(header.toString());
return;
}
this.content = new FutureTask<>(() -> {
final JavaSourceUtil.Handle ch = JavaSourceUtil.createControllerHandle(fileObject, null);
final CompilationController c = (CompilationController) ch.getCompilationController();
c.toPhase(Phase.RESOLVED);
final Element el = handle.resolve(c);
CharSequence doc = getElementDoc(el, c, header, url, false);
if (doc == null) {
computeDocURL(Collections.emptyList(), false, cancel);
doc = header.append(noJavadocFound());
}
return doc.toString();
});
RP.post((Runnable)this.content);
}
}
private ElementJavadoc(URL url, final Callable<Boolean> cancel) {
assert url != null;
this.content = null;
this.docURL = url;
this.handle = null;
this.cpInfo = null;
this.fileObject = null;
this.cancel = cancel;
}
private ElementJavadoc(final String message, final Callable<Boolean> cancel) {
assert message != null;
this.content = new Now(message);
this.docURL = null;
this.handle = null;
this.cpInfo = null;
this.fileObject = null;
this.cancel = cancel;
}
// Private section ---------------------------------------------------------
private StringBuilder getElementHeader(final Element element, final CompilationInfo info) {
final StringBuilder sb = new StringBuilder();
if (element != null) {
sb.append(getContainingClassOrPackageHeader(element, info.getElements(), info.getElementUtilities()));
switch(element.getKind()) {
case METHOD:
case CONSTRUCTOR:
sb.append(getMethodHeader((ExecutableElement)element));
break;
case FIELD:
case ENUM_CONSTANT:
sb.append(getFieldHeader((VariableElement)element));
break;
case CLASS:
case INTERFACE:
case ENUM:
case ANNOTATION_TYPE:
sb.append(getClassHeader((TypeElement)element));
break;
case PACKAGE:
sb.append(getPackageHeader((PackageElement)element));
break;
case MODULE:
sb.append(getModuleHeader((ModuleElement)element));
break;
}
}
return sb;
}
private StringBuilder getContainingClassOrPackageHeader(Element el, Elements elements, ElementUtilities eu) {
StringBuilder sb = new StringBuilder();
if (el.getKind() != ElementKind.PACKAGE && el.getKind() != ElementKind.MODULE) {
TypeElement cls = eu.enclosingTypeElement(el);
if (cls != null) {
switch(cls.getEnclosingElement().getKind()) {
case ANNOTATION_TYPE:
case CLASS:
case ENUM:
case INTERFACE:
case PACKAGE:
sb.append("<font size='+0'><b>"); //NOI18N
createLink(sb, cls, makeNameLineBreakable(cls.getQualifiedName().toString()));
sb.append("</b></font>"); //NOI18N)
}
} else {
PackageElement pkg = elements.getPackageOf(el);
if (pkg != null) {
sb.append("<font size='+0'><b>"); //NOI18N
createLink(sb, pkg, makeNameLineBreakable(pkg.getQualifiedName().toString()));
sb.append("</b></font>"); //NOI18N)
}
}
}
return sb;
}
private String makeNameLineBreakable(String name) {
return name.replace(".", /* ZERO WIDTH SPACE */".&#x200B;");
}
private StringBuilder getMethodHeader(ExecutableElement mdoc) {
final StringBuilder sb = new StringBuilder();
sb.append("<pre>"); //NOI18N
mdoc.getAnnotationMirrors().forEach((annotationDesc) -> {
appendAnnotation(sb, annotationDesc, true);
});
int len = sb.length();
mdoc.getModifiers().stream().filter((modifier) -> (modifier != Modifier.NATIVE)).forEachOrdered((modifier) -> {
sb.append(modifier).append(' '); //NOI18N
});
len = sb.length() - len;
List<? extends TypeParameterElement> typeParameters = mdoc.getTypeParameters();
if (!typeParameters.isEmpty()) {
sb.append("&lt;"); //NOI18N
for (Iterator<? extends TypeParameterElement> it = typeParameters.iterator(); it.hasNext();) {
TypeParameterElement typeParam = it.next();
len += appendType(sb, typeParam.asType(), false, true, false);
if (it.hasNext()) {
sb.append(','); //NOI18N
len++;
}
}
sb.append("&gt; "); //NOI18N
len += 3;
}
if (mdoc.getKind() == ElementKind.CONSTRUCTOR) {
CharSequence name = mdoc.getEnclosingElement().getSimpleName();
len += name.length();
sb.append("<b>").append(name).append("</b>"); //NOI18N
} else {
len += appendType(sb, mdoc.getReturnType(), false, false, false);
CharSequence name = mdoc.getSimpleName();
len += name.length();
sb.append(" <b>").append(name).append("</b>"); //NOI18N
}
if (mdoc.getEnclosingElement().getKind() != ElementKind.ANNOTATION_TYPE) {
sb.append('('); //NOI18N
len++;
for (Iterator<? extends VariableElement> it = mdoc.getParameters().iterator(); it.hasNext();) {
VariableElement param = it.next();
param.getAnnotationMirrors().forEach((annotationDesc) -> {
appendAnnotation(sb, annotationDesc, true);
});
boolean varArg = mdoc.isVarArgs() && !it.hasNext();
appendType(sb, param.asType(), varArg, false, false);
sb.append(' ').append(param.getSimpleName()); //NOI18N
if (it.hasNext()) {
sb.append(",<br>"); //NOI18N
appendSpace(sb, len);
}
}
sb.append(')'); //NOI18N
}
List<? extends TypeMirror> exs = mdoc.getThrownTypes();
if (!exs.isEmpty()) {
sb.append("<br>"); //NOI18N
len = Math.max(0, len - 7);
appendSpace(sb, len);
sb.append("throws "); //NOI18N
for (Iterator<? extends TypeMirror> it = exs.iterator(); it.hasNext();) {
TypeMirror ex = it.next();
appendType(sb, ex, false, false, false);
if (it.hasNext()) {
sb.append(",<br>"); //NOI18N
appendSpace(sb, len);
}
}
}
sb.append("</pre>"); //NOI18N
return sb;
}
private StringBuilder getFieldHeader(VariableElement fdoc) {
StringBuilder sb = new StringBuilder();
sb.append("<pre>"); //NOI18N
fdoc.getAnnotationMirrors().forEach((annotationDesc) -> {
appendAnnotation(sb, annotationDesc, true);
});
fdoc.getModifiers().forEach((modifier) -> {
sb.append(modifier).append(' '); //NOI18N
});
appendType(sb, fdoc.asType(), false, false, false);
sb.append(" <b>").append(fdoc.getSimpleName()).append("</b>"); //NOI18N
String val = null;
try {
val = XMLUtil.toAttributeValue(fdoc.getConstantValue().toString());
} catch (Exception ex) {}
if (val != null && val.length() > 0)
sb.append(" = ").append(val); //NOI18N
sb.append("</pre>"); //NOI18N
return sb;
}
private StringBuilder getClassHeader(TypeElement cdoc) {
StringBuilder sb = new StringBuilder();
sb.append("<pre>"); //NOI18N
cdoc.getAnnotationMirrors().forEach((annotationDesc) -> {
appendAnnotation(sb, annotationDesc, true);
});
for (Modifier modifier : cdoc.getModifiers()) {
switch(cdoc.getKind()) {
case ENUM:
if (modifier == Modifier.FINAL)
continue;
break;
case INTERFACE:
case ANNOTATION_TYPE:
if (modifier == Modifier.ABSTRACT)
continue;
break;
}
sb.append(modifier).append(' '); //NOI18N
}
switch (cdoc.getKind()) {
case ANNOTATION_TYPE:
sb.append("@interface "); //NOI18N
break;
case ENUM:
sb.append("enum "); //NOI18N
break;
case INTERFACE:
sb.append("interface "); //NOI18N
break;
default:
sb.append("class "); //NOI18N
}
sb.append("<b>").append(cdoc.getSimpleName()); //NOI18N
List<? extends TypeParameterElement> typeParams = cdoc.getTypeParameters();
if (!typeParams.isEmpty()) {
sb.append("&lt;"); //NOI18N
for (Iterator<? extends TypeParameterElement> it = typeParams.iterator(); it.hasNext();) {
TypeParameterElement typeParam = it.next();
appendType(sb, typeParam.asType(), false, true, false);
if (it.hasNext())
sb.append(","); //NOI18N
}
sb.append("&gt;"); //NOI18N
}
sb.append("</b>"); //NOi18N
if (cdoc.getKind() != ElementKind.ANNOTATION_TYPE) {
TypeMirror supercls = cdoc.getSuperclass();
if (supercls != null && supercls.getKind() != TypeKind.NONE) {
sb.append("<br>extends "); //NOI18N
appendType(sb, supercls, false, false, false);
}
List<? extends TypeMirror> ifaces = cdoc.getInterfaces();
if (!ifaces.isEmpty()) {
sb.append(cdoc.getKind().isInterface() ? "<br>extends " : "<br>implements "); //NOI18N
for (Iterator<? extends TypeMirror> it = ifaces.iterator(); it.hasNext();) {
TypeMirror iface = it.next();
appendType(sb, iface, false, false, false);
if (it.hasNext())
sb.append(", "); //NOI18N
}
}
}
sb.append("</pre>"); //NOI18N
return sb;
}
private StringBuilder getPackageHeader(PackageElement pdoc) {
StringBuilder sb = new StringBuilder();
sb.append("<pre>"); //NOI18N
pdoc.getAnnotationMirrors().forEach((annotationDesc) -> {
appendAnnotation(sb, annotationDesc, true);
});
sb.append("package <b>").append(pdoc.getQualifiedName()).append("</b>"); //NOI18N
sb.append("</pre>"); //NOI18N
return sb;
}
private StringBuilder getModuleHeader(ModuleElement mdoc) {
StringBuilder sb = new StringBuilder();
sb.append("<pre>"); //NOI18N
mdoc.getAnnotationMirrors().forEach((annotationDesc) -> {
appendAnnotation(sb, annotationDesc, true);
});
sb.append("module <b>").append(mdoc.getQualifiedName()).append("</b>"); //NOI18N
sb.append("</pre>"); //NOI18N
return sb;
}
private void appendAnnotation(StringBuilder sb, AnnotationMirror annotationDesc, boolean topLevel) {
DeclaredType annotationType = annotationDesc.getAnnotationType();
if (annotationType != null && (!topLevel || isDocumented(annotationType))) {
appendType(sb, annotationType, false, false, true);
Map<? extends ExecutableElement, ? extends AnnotationValue> values = annotationDesc.getElementValues();
if (!values.isEmpty()) {
sb.append('('); //NOI18N
for (Iterator<? extends Map.Entry<? extends ExecutableElement, ? extends AnnotationValue>> it = values.entrySet().iterator(); it.hasNext();) {
Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> value = it.next();
createLink(sb, value.getKey(), value.getKey().getSimpleName());
sb.append('='); //NOI18N
appendAnnotationValue(sb, value.getValue());
if (it.hasNext())
sb.append(","); //NOI18N
}
sb.append(')'); //NOI18N
}
if (topLevel)
sb.append("<br>"); //NOI18N
}
}
private boolean isDocumented(DeclaredType annotationType) {
return annotationType.asElement().getAnnotationMirrors().stream().anyMatch(annotationDesc
-> "java.lang.annotation.Documented".contentEquals(((TypeElement)annotationDesc.getAnnotationType().asElement()).getQualifiedName())); //NOI18N
}
private void appendAnnotationValue(StringBuilder sb, AnnotationValue av) {
Object value = av.getValue();
if (value instanceof List) {
List<? extends AnnotationValue> list = (List<? extends AnnotationValue>)value;
if (list.size() > 1)
sb.append('{'); //NOI18N
for (Iterator<? extends AnnotationValue> it = list.iterator(); it.hasNext();) {
AnnotationValue val = it.next();
appendAnnotationValue(sb, val);
if (it.hasNext())
sb.append(","); //NOI18N
}
if (list.size() > 1)
sb.append('}'); //NOI18N
} else if (value instanceof String) {
sb.append('"').append(value).append('"'); //NOI18N
} else if (value instanceof TypeMirror) {
appendType(sb, (TypeMirror)value, false, false, false);
} else if (value instanceof VariableElement) {
createLink(sb, (VariableElement)value, ((VariableElement)value).getSimpleName());
} else if (value instanceof AnnotationMirror) {
appendAnnotation(sb, (AnnotationMirror)value, false);
} else {
sb.append(value.toString());
}
}
private StringBuilder getElementDoc(final Element element, final CompilationInfo info, final StringBuilder header, final URL url, final boolean sync) throws JavadocHelper.RemoteJavadocException {
final StringBuilder sb = new StringBuilder();
if (element != null) {
final List<JavadocHelper.TextStream> pages = JavadocHelper.getJavadoc(
element,
sync ? JavadocHelper.RemoteJavadocPolicy.SPECULATIVE
: JavadocHelper.RemoteJavadocPolicy.USE,
cancel);
try {
boolean localized = false;
boolean remote = false;
for (JavadocHelper.TextStream ts : pages) {
if (docURL == null && !ts.getLocations().isEmpty()) {
docURL = ts.getLocations().get(0);
}
if (docRoot == null) {
docRoot = ts.getDocRoot();
}
localized |= isLocalized(ts.getLocations(), element);
remote |= isRemote(ts, docURL);
}
if (!localized) {
assignSource(element, info, url, header);
}
sb.append(header);
if (!localized) {
Map<Object, StringBuilder> doc = getElementDocFromSource(element, info);
if (!doc.isEmpty()) {
final StringBuilder params = new StringBuilder();
final StringBuilder thrs = new StringBuilder();
doc.entrySet().forEach((entry) -> {
if (entry.getKey() instanceof Element) {
thrs.append("<code>"); //NOI18N
if (((Element)entry.getKey()).getKind() == ElementKind.TYPE_PARAMETER) {
thrs.append(((Element)entry.getKey()).getSimpleName());
} else {
createLink(thrs, (Element)entry.getKey(), ((Element)entry.getKey()).getSimpleName());
}
thrs.append("</code> - "); //NOI18N
thrs.append(entry.getValue());
thrs.append("<br>"); //NOI18N
} else if (entry.getKey() instanceof Integer) {
params.append("<code>").append(getName(element, (int)entry.getKey())).append("</code> - "); //NOI18N
params.append(entry.getValue());
params.append("<br>"); //NOI18N
}
});
sb.append("<p>"); //NOI18N
if (doc.containsKey(DocTree.Kind.DEPRECATED)) {
sb.append("<b>").append(NbBundle.getMessage(ElementJavadoc.class, "JCD-deprecated")).append("</b> <i>").append(doc.get(DocTree.Kind.DEPRECATED)).append("</i><p>"); //NOI18N
}
if (doc.containsKey(null)) {
sb.append(doc.get(null));
}
sb.append("<p>"); //NOI18N
if (doc.containsKey(APINOTE_TAG)) {
sb.append("<b>").append(NbBundle.getMessage(ElementJavadoc.class, "JCD-apinote")).append("</b><blockquote>").append(doc.get(APINOTE_TAG)).append("</blockquote>"); //NOI18N
}
if (doc.containsKey(IMPLSPEC_TAG)) {
sb.append("<b>").append(NbBundle.getMessage(ElementJavadoc.class, "JCD-implspec")).append("</b><blockquote>").append(doc.get(IMPLSPEC_TAG)).append("</blockquote>"); //NOI18N
}
if (doc.containsKey(IMPLNOTE_TAG)) {
sb.append("<b>").append(NbBundle.getMessage(ElementJavadoc.class, "JCD-implnote")).append("</b><blockquote>").append(doc.get(IMPLNOTE_TAG)).append("</blockquote>"); //NOI18N
}
if (params.length() > 0) {
sb.append("<b>").append(NbBundle.getMessage(ElementJavadoc.class, "JCD-params")).append("</b><blockquote>").append(params).append("</blockquote>"); //NOI18N
}
if (doc.containsKey(DocTree.Kind.PARAM)) { //NOI18N
sb.append("<b>").append(NbBundle.getMessage(ElementJavadoc.class, "JCD-typeparams")).append("</b><blockquote>").append(doc.get(DocTree.Kind.PARAM)).append("</blockquote>"); //NOI18N
}
if (doc.containsKey(DocTree.Kind.RETURN)) {
sb.append("<b>").append(NbBundle.getMessage(ElementJavadoc.class, "JCD-returns")).append("</b><blockquote>").append(doc.get(DocTree.Kind.RETURN)).append("</blockquote>"); //NOI18N
}
if (thrs.length() > 0) {
sb.append("<b>").append(NbBundle.getMessage(ElementJavadoc.class, "JCD-throws")).append("</b><blockquote>").append(thrs).append("</blockquote>"); //NOI18N
}
if (doc.containsKey(DocTree.Kind.SINCE)) {
sb.append("<b>").append(NbBundle.getMessage(ElementJavadoc.class, "JCD-since")).append("</b><blockquote>").append(doc.get(DocTree.Kind.SINCE)).append("</blockquote>"); //NOI18N
}
if (doc.containsKey(DocTree.Kind.SEE)) {
sb.append("<b>").append(NbBundle.getMessage(ElementJavadoc.class, "JCD-see")).append("</b><blockquote>").append(doc.get(DocTree.Kind.SEE)).append("</blockquote>"); //NOI18N
}
computeDocURL(pages, sync, cancel);
return sb;
}
}
if (sync && remote) {
throw new JavadocHelper.RemoteJavadocException(null);
}
for (JavadocHelper.TextStream page : pages) {
String jdText = page != null ? HTMLJavadocParser.getJavadocText(page, false) : getURL() != null ? HTMLJavadocParser.getJavadocText(getURL(), false) : null;
if (jdText != null) {
sb.append("<p>"); //NOI18N
sb.append(jdText);
docURL = page.getLocation();
return sb;
}
}
} finally {
pages.forEach((page) -> {
page.close();
});
}
if (element.getKind() == ElementKind.METHOD) {
return getInherited((ExecutableElement)element, (TypeElement)element.getEnclosingElement(), info, method -> {
try {
return getElementDoc(method, info, header, url, sync);
} catch (JavadocHelper.RemoteJavadocException rje) {
ElementJavadoc.<Void, RuntimeException>sthrow(rje);
}
return null;
});
}
}
return null;
}
private Map<Object, StringBuilder> getElementDocFromSource(final Element element, final CompilationInfo info) {
final Map<Object, StringBuilder> ret = new LinkedHashMap<>();
final ElementHandle<? extends Element> eh = ElementHandle.create(element);
FileObject fo = SourceUtils.getFile(eh, cpInfo);
if (fo != null && fo.isFolder() && element.getKind() == ElementKind.PACKAGE) {
fo = fo.getFileObject("package-info.java"); // NOI18N
}
if (fo != null && fo != info.getFileObject()) {
try {
JavaSource js = JavaSource.forFileObject(fo);
if (js != null) {
final Map<Object, StringBuilder> out = new HashMap<>();
js.runUserActionTask(controller -> {
controller.toPhase(Phase.ELEMENTS_RESOLVED);
Element e = eh.resolve(controller);
if (e != null) {
getElementDocFromSource(e, controller).entrySet().forEach((entry) -> {
Object key = entry.getKey();
if (key instanceof Element) {
key = ElementHandle.create((Element)key);
}
out.put(key, entry.getValue());
});
}
},true);
out.entrySet().forEach((entry) -> {
Object key = entry.getKey();
if (key instanceof ElementHandle) {
Element e = ((ElementHandle)key).resolve(info);
if (e != null) {
ret.put(e, entry.getValue());
}
} else {
ret.put(key, entry.getValue());
}
});
}
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
return ret;
}
final DocCommentTree doc = info.getDocTrees().getDocCommentTree(element);
if (doc != null) {
final TreePath path = info.getDocTrees().getPath(element);
List<? extends DocTree> body = doc.getFullBody();
List<? extends DocTree> tags = doc.getBlockTags();
AtomicBoolean inheritedDocNeeded = new AtomicBoolean(false);
Map<Object, StringBuilder> inheritedDoc = null;
Function<ExecutableElement, Map<Object, StringBuilder>> funct = method -> {
return getElementDocFromSource(method, info);
};
if (element.getKind() == ElementKind.METHOD) {
if (body.isEmpty()) {
inheritedDocNeeded.set(true);
} else {
new DocTreeScanner<Void, Void>() {
@Override
public Void visitInheritDoc(InheritDocTree node, Void p) {
inheritedDocNeeded.set(true);
return null;
}
}.scan(doc, null);
}
if (inheritedDocNeeded.get()) {
inheritedDoc = getInherited((ExecutableElement)element, (TypeElement)element.getEnclosingElement(), info, funct);
}
}
if (body.isEmpty()) {
StringBuilder inhBody = inheritedDoc != null ? inheritedDoc.get(null) : null;
if (inhBody != null) {
ret.put(null, inhBody);
}
} else {
ret.put(null, inlineTags(body, path, doc, info.getDocTrees(), inheritedDoc != null ? inheritedDoc.get(null) : null));
}
StringBuilder sb;
for (DocTree tag : tags) {
switch(tag.getKind()) {
case PARAM:
ParamTree paramTag = (ParamTree)tag;
if (paramTag.isTypeParameter()) {
sb = ret.get(DocTree.Kind.PARAM);
if (sb == null) {
sb = new StringBuilder();
ret.put(DocTree.Kind.PARAM, sb);
}
sb.append("<code>").append(paramTag.getName().getName()).append("</code>"); //NOI18N
List<? extends DocTree> desc = paramTag.getDescription();
if (!desc.isEmpty()) {
sb.append(" - "); //NOI18N
sb.append(inlineTags(desc, path, doc, info.getDocTrees(), null));
}
sb.append("<br>"); //NOI18N
} else {
List<? extends DocTree> desc = paramTag.getDescription();
if (!desc.isEmpty()) {
Integer idx = getIdx(element, paramTag.getName().getName());
if (idx != null) {
sb = ret.get(idx);
if (sb == null) {
sb = new StringBuilder();
ret.put(idx, sb);
}
sb.append(inlineTags(desc, path, doc, info.getDocTrees(), inheritedDoc != null ? inheritedDoc.get(idx) : null));
}
}
}
break;
case THROWS:
ThrowsTree throwsTag = (ThrowsTree)tag;
Element e = info.getDocTrees().getElement(DocTreePath.getPath(path, doc, throwsTag.getExceptionName()));
if (e != null) {
List<? extends DocTree> desc = throwsTag.getDescription();
if (!desc.isEmpty()) {
sb = ret.get(e);
if (sb == null) {
sb = new StringBuilder();
ret.put(e, sb);
}
sb.append(inlineTags(desc, path, doc, info.getDocTrees(), inheritedDoc != null ? inheritedDoc.get(e) : null));
}
}
break;
case RETURN:
sb = ret.get(DocTree.Kind.RETURN);
if (sb == null) {
sb = new StringBuilder();
ret.put(DocTree.Kind.RETURN, sb);
}
sb.append(inlineTags(((ReturnTree)tag).getDescription(), path, doc, info.getDocTrees(), inheritedDoc != null ? inheritedDoc.get(DocTree.Kind.RETURN) : null));
break;
case SEE:
sb = ret.get(DocTree.Kind.SEE);
if (sb == null) {
sb = new StringBuilder();
ret.put(DocTree.Kind.SEE, sb);
}
SeeTree seeTag = (SeeTree)tag;
if (sb.length() > 0) {
sb.append(", "); //NOI18N
}
sb.append(inlineTags(seeTag.getReference(), path, doc, info.getDocTrees(), null));
break;
case SINCE:
sb = ret.get(DocTree.Kind.SINCE);
if (sb == null) {
sb = new StringBuilder();
ret.put(DocTree.Kind.SINCE, sb);
}
sb.append(inlineTags(((SinceTree)tag).getBody(), path, doc, info.getDocTrees(), null));
break;
case DEPRECATED:
sb = ret.get(DocTree.Kind.DEPRECATED);
if (sb == null) {
sb = new StringBuilder();
ret.put(DocTree.Kind.DEPRECATED, sb);
}
sb.append(inlineTags(((DeprecatedTree)tag).getBody(), path, doc, info.getDocTrees(), null));
break;
case UNKNOWN_BLOCK_TAG:
UnknownBlockTagTree unTag = (UnknownBlockTagTree)tag;
switch (unTag.getTagName()) {
case APINOTE_TAG:
sb = ret.get(APINOTE_TAG);
if (sb == null) {
sb = new StringBuilder();
ret.put(APINOTE_TAG, sb);
}
sb.append(inlineTags(unTag.getContent(), path, doc, info.getDocTrees(), null));
break;
case IMPLSPEC_TAG:
sb = ret.get(IMPLSPEC_TAG);
if (sb == null) {
sb = new StringBuilder();
ret.put(IMPLSPEC_TAG, sb);
}
sb.append(inlineTags(unTag.getContent(), path, doc, info.getDocTrees(), null));
break;
case IMPLNOTE_TAG:
sb = ret.get(IMPLNOTE_TAG);
if (sb == null) {
sb = new StringBuilder();
ret.put(IMPLNOTE_TAG, sb);
}
sb.append(inlineTags(unTag.getContent(), path, doc, info.getDocTrees(), null));
break;
}
break;
}
}
if (element.getKind() == ElementKind.METHOD) {
if (!ret.containsKey(DocTree.Kind.RETURN)) {
//If the method does not have a return javadoc then we need to get javadoc from parent.
if (inheritedDoc == null && !inheritedDocNeeded.getAndSet(true)) {
inheritedDoc = getInherited((ExecutableElement)element, (TypeElement)element.getEnclosingElement(), info, funct);
}
if (inheritedDoc != null && inheritedDoc.containsKey(DocTree.Kind.RETURN)) {
ret.put(DocTree.Kind.RETURN, new StringBuilder(inheritedDoc.get(DocTree.Kind.RETURN)));
}
}
for (int i = 0; i < ((ExecutableElement)element).getParameters().size(); i++) {
//Check every parameters in the method. If the current parameter has javadoc we do nothing.
//If the current parameter does not have javadoc we try to get javadoc from the parent.
if (!ret.containsKey(i)) {
if (inheritedDoc == null && !inheritedDocNeeded.getAndSet(true)) {
inheritedDoc = getInherited((ExecutableElement)element, (TypeElement)element.getEnclosingElement(), info, funct);
}
if (inheritedDoc != null && inheritedDoc.containsKey(i)) {
ret.put(i, new StringBuilder(inheritedDoc.get(i)));
}
}
}
for (TypeMirror thrownType : ((ExecutableElement)element).getThrownTypes()) {
//Check every throwables listed in the method. If for the current throwable there is not a javadoc try to get it from the parent.
//If for the current throwable there is a javadoc we show the current javadoc.
Element e = thrownType.getKind() == TypeKind.TYPEVAR ? ((TypeVariable)thrownType).asElement() : ((DeclaredType)thrownType).asElement();
if (!ret.containsKey(e)) {
if (inheritedDoc == null && !inheritedDocNeeded.getAndSet(true)) {
inheritedDoc = getInherited((ExecutableElement)element, (TypeElement)element.getEnclosingElement(), info, funct);
}
if (inheritedDoc != null && inheritedDoc.containsKey(e)) {
ret.put(e, new StringBuilder(inheritedDoc.get(e)));
}
}
}
}
}
return ret;
}
private <T> T getInherited(final ExecutableElement element, final TypeElement type, final CompilationInfo info, final Function<ExecutableElement, T> funct) {
final Elements elements = info.getElements();
for (TypeMirror iface : type.getInterfaces()) {
for (ExecutableElement method : ElementFilter.methodsIn(((DeclaredType)iface).asElement().getEnclosedElements())) {
if (elements.overrides(element, method, (TypeElement)element.getEnclosingElement())) {
T ret = funct.apply(method);
if (ret != null) {
return ret;
}
}
}
}
for (TypeMirror iface : type.getInterfaces()) {
T ret = getInherited(element, (TypeElement)((DeclaredType)iface).asElement(), info, funct);
if (ret != null) {
return ret;
}
}
TypeMirror superclass = type.getSuperclass();
if (superclass.getKind() == TypeKind.DECLARED) {
for (ExecutableElement method : ElementFilter.methodsIn(((DeclaredType)superclass).asElement().getEnclosedElements())) {
if (elements.overrides(element, method, (TypeElement)element.getEnclosingElement())) {
T ret = funct.apply(method);
if (ret != null) {
return ret;
}
}
}
return getInherited(element, (TypeElement)((DeclaredType)superclass).asElement(), info, funct);
}
return null;
}
private void computeDocURL(
final List<? extends JavadocHelper.TextStream> pages,
final boolean sync,
final Callable<Boolean> cancel) {
class ComputeURL implements Callable<Void> {
private final boolean remote;
private ComputeURL(@NonNull final boolean remote) {
this.remote = remote;
}
@Override
public Void call() throws Exception {
if (cancel == null || cancel.call() != Boolean.TRUE) {
if (!remote && pages.size() > 1) {
throw new JavadocHelper.RemoteJavadocException(null);
}
for (JavadocHelper.TextStream page : pages) {
final URL loc = page.getLocation(
remote ?
JavadocHelper.RemoteJavadocPolicy.USE :
JavadocHelper.RemoteJavadocPolicy.EXCEPTION);
if (loc != null) {
docURL = loc;
break;
}
}
}
return null;
}
}
if (docURL == null) {
if (sync) {
try {
new ComputeURL(false).call();
} catch (JavadocHelper.RemoteJavadocException e) {
RP.submit(new ComputeURL(true));
} catch (Exception e) {
Exceptions.printStackTrace(e);
}
} else {
try {
new ComputeURL(true).call();
} catch (Exception e) {
Exceptions.printStackTrace(e);
}
}
}
}
private String noJavadocFound() {
if (handle != null) {
final List<ClassPath> cps = new ArrayList<>(2);
ClassPath cp = cpInfo.getClassPath(ClasspathInfo.PathKind.BOOT);
if (cp != null) {
cps.add(cp);
}
cp = cpInfo.getClassPath(ClasspathInfo.PathKind.COMPILE);
if (cp != null) {
cps.add(cp);
}
cp = ClassPathSupport.createProxyClassPath(cps.toArray(new ClassPath[cps.size()]));
String toSearch = SourceUtils.getJVMSignature(handle)[0].replace('.', '/');
if (handle.getKind() != ElementKind.PACKAGE) {
toSearch += ".class"; //NOI18N
}
final FileObject resource = cp.findResource(toSearch);
if (resource != null) {
final FileObject root = cp.findOwnerRoot(resource);
try {
final URL rootURL = root.getURL();
if (JavadocForBinaryQuery.findJavadoc(rootURL).getRoots().length == 0) {
FileObject userRoot = FileUtil.getArchiveFile(root);
if (userRoot == null) {
userRoot = root;
}
return NbBundle.getMessage(
ElementJavadoc.class,
"javadoc_content_not_found_attach",
rootURL.toExternalForm(),
FileUtil.getFileDisplayName(userRoot));
}
} catch (FileStateInvalidException ex) {
Exceptions.printStackTrace(ex);
}
}
}
return NbBundle.getMessage(ElementJavadoc.class, "javadoc_content_not_found"); //NOI18N
}
private StringBuilder inlineTags(List<? extends DocTree> tags, TreePath docPath, DocCommentTree doc, DocTrees trees, CharSequence inherited) {
StringBuilder sb = new StringBuilder();
for (DocTree tag : tags) {
switch (tag.getKind()) {
case REFERENCE:
ReferenceTree refTag = (ReferenceTree)tag;
appendReference(sb, refTag, null, docPath, doc, trees);
break;
case LINK_PLAIN:
LinkTree linkTag = (LinkTree)tag;
appendReference(sb, linkTag.getReference(), linkTag.getLabel(), docPath, doc, trees);
break;
case LINK:
linkTag = (LinkTree)tag;
sb.append("<code>"); //NOI18N
appendReference(sb, linkTag.getReference(), linkTag.getLabel(), docPath, doc, trees);
sb.append("</code>"); //NOI18N
break;
case CODE:
LiteralTree codeTag = (LiteralTree)tag;
sb.append("<code>"); //NOI18N
try {
sb.append(XMLUtil.toElementContent(codeTag.getBody().getBody()));
} catch (IOException ioe) {}
sb.append("</code>"); //NOI18N
break;
case LITERAL:
LiteralTree literalTag = (LiteralTree)tag;
try {
sb.append(XMLUtil.toElementContent(literalTag.getBody().getBody()));
} catch (IOException ioe) {}
break;
case VALUE:
ValueTree valueTag = (ValueTree)tag;
ReferenceTree ref = valueTag.getReference();
Element element = ref == null
? trees.getElement(docPath)
: trees.getElement(DocTreePath.getPath(docPath, doc, ref));
if (element != null && element.getKind().isField()) {
try {
sb.append(XMLUtil.toElementContent(((VariableElement)element).getConstantValue().toString()));
} catch (IOException ioe) {}
}
break;
case INHERIT_DOC:
if (inherited != null) {
sb.append(inherited);
}
break;
case START_ELEMENT:
StartElementTree startTag = (StartElementTree)tag;
List<? extends DocTree> attrs = startTag.getAttributes();
sb.append('<').append(startTag.getName()).append(attrs.isEmpty() ? "" : " ").append(inlineTags(attrs, docPath, doc, trees, null)).append(startTag.isSelfClosing() ? "/>" : ">"); //NOI18N
break;
case END_ELEMENT:
EndElementTree endTag = (EndElementTree)tag;
sb.append("</").append(endTag.getName()).append('>'); //NOI18N
break;
case ATTRIBUTE:
AttributeTree attrTag = (AttributeTree)tag;
sb.append(attrTag.getName());
String quote;
switch (attrTag.getValueKind()) {
case EMPTY:
quote = null;
break;
case UNQUOTED:
quote = ""; //NOI18N
break;
case SINGLE:
quote = "'"; //NOI18N
break;
case DOUBLE:
quote = "\""; //NOI18N
break;
default:
throw new AssertionError();
}
if (quote != null) {
sb.append("=").append(quote).append(inlineTags(attrTag.getValue(), docPath, doc, trees, null)).append(quote); //NOI18N
}
break;
case DOC_ROOT:
if (docRoot != null) {
sb.append(docRoot);
}
break;
case ENTITY:
EntityTree entityTag = (EntityTree)tag;
sb.append('&').append(entityTag.getName()).append(';');
break;
case TEXT:
TextTree ttag = (TextTree)tag;
sb.append(ttag.getBody());
}
}
return sb;
}
private void appendReference(StringBuilder sb, ReferenceTree ref, List<? extends DocTree> label, TreePath docPath, DocCommentTree doc, DocTrees trees) {
String sig = ref.getSignature();
if (sig != null && sig.length() > 0) {
if (sig.charAt(0) == '#') { //NOI18N
sig = sig.substring(1);
}
sig = sig.replace('#', '.'); //NOI18N
}
Element element = trees.getElement(DocTreePath.getPath(docPath, doc, ref));
if (element != null) {
createLink(sb, element, label == null || label.isEmpty() ? sig : inlineTags(label, docPath, doc, trees, null)); //NOI18N
} else {
sb.append(label == null || label.isEmpty() ? sig : inlineTags(label, docPath, doc, trees, null));
}
}
private void appendType(StringBuilder sb, TypeMirror type, boolean varArg) {
switch (type.getKind()) {
case ARRAY:
appendType(sb, ((ArrayType)type).getComponentType(), false);
sb.append(varArg ? "..." : "[]"); //NOI18N
break;
case DECLARED:
sb.append(((TypeElement)((DeclaredType)type).asElement()).getQualifiedName());
break;
default:
sb.append(type);
}
}
private void appendSpace(StringBuilder sb, int length) {
while (length-- >= 0) {
sb.append(' '); //NOI18N
}
}
private int appendType(StringBuilder sb, TypeMirror type, boolean varArg, boolean typeVar, boolean annotation) {
int len = 0;
switch(type.getKind()) {
case WILDCARD:
WildcardType wt = (WildcardType)type;
sb.append('?'); //NOI18N
len++;
TypeMirror bound = wt.getExtendsBound();
if (bound != null) {
sb.append(" extends "); //NOI18N
len += 9;
len += appendType(sb, bound, false, false, false);
}
bound = wt.getSuperBound();
if (bound != null) {
sb.append(" super "); //NOI18N
len += 7;
len += appendType(sb, bound, false, false, false);
}
break;
case TYPEVAR:
TypeVariable tv = (TypeVariable)type;
len += createLink(sb, null, tv.asElement().getSimpleName());
bound = tv.getUpperBound();
if (typeVar && bound != null &&
(bound.getKind() != TypeKind.DECLARED || !bound.getAnnotationMirrors().isEmpty()
|| !"java.lang.Object".contentEquals(((TypeElement)((DeclaredType)bound).asElement()).getQualifiedName()))) {
sb.append(" extends "); //NOI18N
len += 9;
len += appendType(sb, bound, false, false, false);
}
break;
case INTERSECTION:
IntersectionType it = (IntersectionType)type;
for (Iterator<? extends TypeMirror> iter = it.getBounds().iterator(); iter.hasNext();) {
bound = iter.next();
len += appendType(sb, bound, false, false, false);
if (iter.hasNext()) {
sb.append(" & "); //NOI18N
len += 3;
}
}
break;
case DECLARED:
DeclaredType dt = (DeclaredType)type;
Element el = dt.asElement();
len += createLink(sb, el, annotation && el.getKind() == ElementKind.ANNOTATION_TYPE ? "@" + el.getSimpleName() : el.getSimpleName()); //NOI18N
List<? extends TypeMirror> typeArgs = dt.getTypeArguments();
if (!typeArgs.isEmpty()) {
sb.append("&lt;"); //NOI18N
for (Iterator<? extends TypeMirror> iter = typeArgs.iterator(); iter.hasNext();) {
TypeMirror typeArg = iter.next();
len += appendType(sb, typeArg, false, false, false);
if (iter.hasNext()) {
sb.append(","); //NOI18N
len++;
}
}
sb.append("&gt;"); //NOI18N
len += 2;
}
break;
case ARRAY:
ArrayType at = (ArrayType)type;
len += appendType(sb, at.getComponentType(), false, false, false);
if (varArg) {
sb.append("..."); //NOI18N
len += 3;
} else {
sb.append("[]"); //NOI18N
len += 2;
}
break;
case BOOLEAN:
sb.append("boolean");
len += 7;
break;
case CHAR:
sb.append("char");
len += 4;
break;
case BYTE:
sb.append("byte");
len += 4;
break;
case DOUBLE:
sb.append("double");
len += 6;
break;
case FLOAT:
sb.append("float");
len += 5;
break;
case INT:
sb.append("int");
len += 3;
break;
case LONG:
sb.append("long");
len += 4;
break;
case SHORT:
sb.append("short");
len += 5;
break;
case VOID:
sb.append("void");
len += 4;
break;
}
return len;
}
private Integer getIdx(Element e, Name n) {
if (e instanceof ExecutableElement && n != null) {
int idx = 0;
for (VariableElement par : ((ExecutableElement)e).getParameters()) {
if (n.contentEquals(par.getSimpleName())) {
return idx;
}
idx++;
}
}
return null;
}
private Name getName(Element e, int idx) {
return e instanceof ExecutableElement ? ((ExecutableElement)e).getParameters().get(idx).getSimpleName() : null;
}
private int createLink(StringBuilder sb, Element e, CharSequence text) {
if (e != null && e.asType().getKind() != TypeKind.ERROR) {
String link = "*" + linkCounter++; //NOI18N
links.put(link, ElementHandle.create(e));
sb.append("<a href='").append(link).append("'>"); //NOI18N
}
sb.append(text);
if (e != null)
sb.append("</a>"); //NOI18N
return text.length();
}
private CharSequence getFragment(Element e) {
StringBuilder sb = new StringBuilder();
if (!e.getKind().isClass() && !e.getKind().isInterface()) {
if (e.getKind() == ElementKind.CONSTRUCTOR) {
sb.append(e.getEnclosingElement().getSimpleName());
} else {
sb.append(e.getSimpleName());
}
if (e.getKind() == ElementKind.METHOD || e.getKind() == ElementKind.CONSTRUCTOR) {
ExecutableElement ee = (ExecutableElement)e;
sb.append('('); //NOI18N
for (Iterator<? extends VariableElement> it = ee.getParameters().iterator(); it.hasNext();) {
VariableElement param = it.next();
appendType(sb, param.asType(), ee.isVarArgs() && !it.hasNext());
if (it.hasNext())
sb.append(", ");
}
sb.append(')'); //NOI18N
}
}
return sb;
}
private static boolean isRemote(final JavadocHelper.TextStream page, final URL url) {
if (page != null) {
if (page.getLocations().stream().anyMatch((loc) -> (loc.toString().startsWith("http")))) { //NOI18N
return true;
}
}
return url != null ? url.toString().startsWith("http") : false; //NOI18N
}
private static boolean isLocalized(final List<? extends URL> docUrls, final Element element) {
return docUrls.stream().anyMatch((docUrl) -> (isLocalized(docUrl, element)));
}
private static boolean isLocalized(final URL docURL, final Element element) {
if (docURL == null) {
return false;
}
Element pkg = element;
while (pkg.getKind() != ElementKind.PACKAGE) {
pkg = pkg.getEnclosingElement();
if (pkg == null) {
return false;
}
}
String pkgBinName = ((PackageElement)pkg).getQualifiedName().toString();
String surl = docURL.toString();
int index = surl.lastIndexOf('/'); //NOI18N
if (index < 0) {
return false;
}
index-=(pkgBinName.length()+1);
if (index < 0) {
return false;
}
index-=API.length();
if (index < 0 || !surl.regionMatches(index,API,0,API.length())) {
return false;
}
int index2 = surl.lastIndexOf('/', index-1); //NOI18N
if (index2 < 0) {
return false;
}
String lang = surl.substring(index2+1, index);
return LANGS.contains(lang);
}
@SuppressWarnings("unchecked")
private static <R, T extends Throwable> R sthrow (@NonNull final Throwable t) throws T {
throw (T) t;
}
private static final class Now implements Future<String> {
private final String value;
Now(final String value) {
this.value = value;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return false;
}
@Override
public boolean isCancelled() {
return false;
}
@Override
public boolean isDone() {
return true;
}
@Override
public String get() throws InterruptedException, ExecutionException {
return value;
}
@Override
public String get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return value;
}
}
private void assignSource(
@NonNull final Element element,
@NonNull final CompilationInfo compilationInfo,
@NullAllowed final URL url,
@NonNull final StringBuilder content) {
final FileObject fo = SourceUtils.getFile(element, compilationInfo.getClasspathInfo());
if (fo != null) {
if (docURL == null && goToSource == null) {
content.insert(0, "<base href=\"" + fo.toURL() + "\"></base>"); //NOI18N
}
goToSource = new AbstractAction() {
@Override
public void actionPerformed(ActionEvent evt) {
ElementOpen.open(fo, handle);
}
};
}
if (url != null) {
docURL = url;
}
}
}