8290243: move seeTagToContent from HtmlDocletWriter to TagletWriterImpl
Reviewed-by: prappo
This commit is contained in:
parent
15943e4242
commit
8179a191f0
@ -386,7 +386,7 @@ public class HtmlDocletWriter {
|
||||
* @param context the enclosing context
|
||||
* @return a TagletWriter
|
||||
*/
|
||||
public TagletWriter getTagletWriterInstance(TagletWriterImpl.Context context) {
|
||||
public TagletWriterImpl getTagletWriterInstance(TagletWriterImpl.Context context) {
|
||||
return new TagletWriterImpl(this, context);
|
||||
}
|
||||
|
||||
@ -971,316 +971,6 @@ public class HtmlDocletWriter {
|
||||
return label;
|
||||
}
|
||||
|
||||
public Content seeTagToContent(Element element, DocTree see, TagletWriterImpl.Context context) {
|
||||
Kind kind = see.getKind();
|
||||
CommentHelper ch = utils.getCommentHelper(element);
|
||||
String tagName = ch.getTagName(see);
|
||||
|
||||
String refText;
|
||||
List<? extends DocTree> label;
|
||||
switch (kind) {
|
||||
case LINK, LINK_PLAIN -> {
|
||||
// {@link[plain] reference label...}
|
||||
LinkTree lt = (LinkTree) see;
|
||||
var linkRef = lt.getReference();
|
||||
if (linkRef == null) {
|
||||
messages.warning(ch.getDocTreePath(see),"doclet.link.no_reference");
|
||||
return invalidTagOutput(resources.getText("doclet.tag.invalid_input", lt.toString()),
|
||||
Optional.empty());
|
||||
}
|
||||
refText = linkRef.toString();
|
||||
label = lt.getLabel();
|
||||
}
|
||||
|
||||
case SEE -> {
|
||||
List<? extends DocTree> ref = ((SeeTree) see).getReference();
|
||||
assert !ref.isEmpty();
|
||||
DocTree ref0 = ref.get(0);
|
||||
switch (ref0.getKind()) {
|
||||
case TEXT, START_ELEMENT -> {
|
||||
// @see "Reference"
|
||||
// @see <a href="...">...</a>
|
||||
return commentTagsToContent(element, ref, false, false);
|
||||
}
|
||||
case REFERENCE -> {
|
||||
// @see reference label...
|
||||
refText = ref0.toString();
|
||||
label = ref.subList(1, ref.size());
|
||||
}
|
||||
case ERRONEOUS -> {
|
||||
return invalidTagOutput(resources.getText("doclet.tag.invalid_input",
|
||||
ref0.toString()),
|
||||
Optional.empty());
|
||||
}
|
||||
default ->
|
||||
throw new IllegalStateException(ref0.getKind().toString());
|
||||
}
|
||||
}
|
||||
|
||||
default ->
|
||||
throw new IllegalStateException(kind.toString());
|
||||
}
|
||||
|
||||
boolean isLinkPlain = kind == LINK_PLAIN;
|
||||
Content labelContent = plainOrCode(isLinkPlain,
|
||||
commentTagsToContent(element, label, context));
|
||||
|
||||
// The signature from the @see tag. We will output this text when a label is not specified.
|
||||
Content text = plainOrCode(isLinkPlain,
|
||||
Text.of(Objects.requireNonNullElse(ch.getReferencedSignature(see), "")));
|
||||
|
||||
TypeElement refClass = ch.getReferencedClass(see);
|
||||
Element refMem = ch.getReferencedMember(see);
|
||||
String refMemName = ch.getReferencedMemberName(see);
|
||||
|
||||
if (refMemName == null && refMem != null) {
|
||||
refMemName = refMem.toString();
|
||||
}
|
||||
if (refClass == null) {
|
||||
ModuleElement refModule = ch.getReferencedModule(see);
|
||||
if (refModule != null && utils.isIncluded(refModule)) {
|
||||
return getModuleLink(refModule, labelContent.isEmpty() ? text : labelContent);
|
||||
}
|
||||
//@see is not referencing an included class
|
||||
PackageElement refPackage = ch.getReferencedPackage(see);
|
||||
if (refPackage != null && utils.isIncluded(refPackage)) {
|
||||
//@see is referencing an included package
|
||||
if (labelContent.isEmpty())
|
||||
labelContent = plainOrCode(isLinkPlain,
|
||||
Text.of(refPackage.getQualifiedName()));
|
||||
return getPackageLink(refPackage, labelContent);
|
||||
} else {
|
||||
// @see is not referencing an included class, module or package. Check for cross links.
|
||||
String refModuleName = ch.getReferencedModuleName(see);
|
||||
DocLink elementCrossLink = (refPackage != null) ? getCrossPackageLink(refPackage) :
|
||||
(configuration.extern.isModule(refModuleName))
|
||||
? getCrossModuleLink(utils.elementUtils.getModuleElement(refModuleName))
|
||||
: null;
|
||||
if (elementCrossLink != null) {
|
||||
// Element cross link found
|
||||
return links.createExternalLink(elementCrossLink,
|
||||
(labelContent.isEmpty() ? text : labelContent));
|
||||
} else {
|
||||
// No cross link found so print warning
|
||||
if (!configuration.isDocLintReferenceGroupEnabled()) {
|
||||
messages.warning(ch.getDocTreePath(see),
|
||||
"doclet.see.class_or_package_not_found",
|
||||
"@" + tagName,
|
||||
refText);
|
||||
}
|
||||
return invalidTagOutput(resources.getText("doclet.tag.invalid", tagName),
|
||||
Optional.of(labelContent.isEmpty() ? text: labelContent));
|
||||
}
|
||||
}
|
||||
} else if (refMemName == null) {
|
||||
// Must be a class reference since refClass is not null and refMemName is null.
|
||||
if (labelContent.isEmpty()) {
|
||||
TypeMirror referencedType = ch.getReferencedType(see);
|
||||
if (utils.isGenericType(referencedType)) {
|
||||
// This is a generic type link, use the TypeMirror representation.
|
||||
return plainOrCode(isLinkPlain, getLink(
|
||||
new HtmlLinkInfo(configuration, HtmlLinkInfo.Kind.DEFAULT, referencedType)));
|
||||
}
|
||||
labelContent = plainOrCode(isLinkPlain, Text.of(utils.getSimpleName(refClass)));
|
||||
}
|
||||
return getLink(new HtmlLinkInfo(configuration, HtmlLinkInfo.Kind.DEFAULT, refClass)
|
||||
.label(labelContent));
|
||||
} else if (refMem == null) {
|
||||
// Must be a member reference since refClass is not null and refMemName is not null.
|
||||
// However, refMem is null, so this referenced member does not exist.
|
||||
return (labelContent.isEmpty() ? text: labelContent);
|
||||
} else {
|
||||
// Must be a member reference since refClass is not null and refMemName is not null.
|
||||
// refMem is not null, so this @see tag must be referencing a valid member.
|
||||
TypeElement containing = utils.getEnclosingTypeElement(refMem);
|
||||
|
||||
// Find the enclosing type where the method is actually visible
|
||||
// in the inheritance hierarchy.
|
||||
ExecutableElement overriddenMethod = null;
|
||||
if (refMem.getKind() == ElementKind.METHOD) {
|
||||
VisibleMemberTable vmt = configuration.getVisibleMemberTable(containing);
|
||||
overriddenMethod = vmt.getOverriddenMethod((ExecutableElement)refMem);
|
||||
|
||||
if (overriddenMethod != null)
|
||||
containing = utils.getEnclosingTypeElement(overriddenMethod);
|
||||
}
|
||||
if (refText.trim().startsWith("#") &&
|
||||
! (utils.isPublic(containing) || utils.isLinkable(containing))) {
|
||||
// Since the link is relative and the holder is not even being
|
||||
// documented, this must be an inherited link. Redirect it.
|
||||
// The current class either overrides the referenced member or
|
||||
// inherits it automatically.
|
||||
if (this instanceof ClassWriterImpl writer) {
|
||||
containing = writer.getTypeElement();
|
||||
} else if (!utils.isPublic(containing)) {
|
||||
messages.warning(
|
||||
ch.getDocTreePath(see), "doclet.see.class_or_package_not_accessible",
|
||||
tagName, utils.getFullyQualifiedName(containing));
|
||||
} else {
|
||||
if (!configuration.isDocLintReferenceGroupEnabled()) {
|
||||
messages.warning(
|
||||
ch.getDocTreePath(see), "doclet.see.class_or_package_not_found",
|
||||
tagName, refText);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (configuration.currentTypeElement != containing) {
|
||||
refMemName = (utils.isConstructor(refMem))
|
||||
? refMemName
|
||||
: utils.getSimpleName(containing) + "." + refMemName;
|
||||
}
|
||||
if (utils.isExecutableElement(refMem)) {
|
||||
if (refMemName.indexOf('(') < 0) {
|
||||
refMemName += utils.makeSignature((ExecutableElement) refMem, null, true);
|
||||
}
|
||||
if (overriddenMethod != null) {
|
||||
// The method to actually link.
|
||||
refMem = overriddenMethod;
|
||||
}
|
||||
}
|
||||
|
||||
return getDocLink(HtmlLinkInfo.Kind.SEE_TAG, containing,
|
||||
refMem, (labelContent.isEmpty()
|
||||
? plainOrCode(isLinkPlain, Text.of(refMemName))
|
||||
: labelContent), null, false);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: this method and seeTagToContent share much of the code; consider factoring common pieces out
|
||||
public Content linkToContent(Element referrer, Element target, String targetSignature, String text) {
|
||||
CommentHelper ch = utils.getCommentHelper(referrer);
|
||||
|
||||
boolean isLinkPlain = false; // TODO: for now
|
||||
Content labelContent = plainOrCode(isLinkPlain, Text.of(text));
|
||||
|
||||
TypeElement refClass = ch.getReferencedClass(target);
|
||||
Element refMem = ch.getReferencedMember(target);
|
||||
String refMemName = ch.getReferencedMemberName(targetSignature);
|
||||
|
||||
if (refMemName == null && refMem != null) {
|
||||
refMemName = refMem.toString();
|
||||
}
|
||||
if (refClass == null) {
|
||||
ModuleElement refModule = ch.getReferencedModule(target);
|
||||
if (refModule != null && utils.isIncluded(refModule)) {
|
||||
return getModuleLink(refModule, labelContent);
|
||||
}
|
||||
//@see is not referencing an included class
|
||||
PackageElement refPackage = ch.getReferencedPackage(target);
|
||||
if (refPackage != null && utils.isIncluded(refPackage)) {
|
||||
//@see is referencing an included package
|
||||
if (labelContent.isEmpty())
|
||||
labelContent = plainOrCode(isLinkPlain,
|
||||
Text.of(refPackage.getQualifiedName()));
|
||||
return getPackageLink(refPackage, labelContent);
|
||||
} else {
|
||||
// @see is not referencing an included class, module or package. Check for cross links.
|
||||
String refModuleName = ch.getReferencedModuleName(targetSignature);
|
||||
DocLink elementCrossLink = (refPackage != null) ? getCrossPackageLink(refPackage) :
|
||||
(configuration.extern.isModule(refModuleName))
|
||||
? getCrossModuleLink(utils.elementUtils.getModuleElement(refModuleName))
|
||||
: null;
|
||||
if (elementCrossLink != null) {
|
||||
// Element cross link found
|
||||
return links.createExternalLink(elementCrossLink, labelContent);
|
||||
} else {
|
||||
// No cross link found so print warning
|
||||
// TODO:
|
||||
// messages.warning(ch.getDocTreePath(see),
|
||||
// "doclet.see.class_or_package_not_found",
|
||||
// "@" + tagName,
|
||||
// seeText);
|
||||
return labelContent;
|
||||
}
|
||||
}
|
||||
} else if (refMemName == null) {
|
||||
// Must be a class reference since refClass is not null and refMemName is null.
|
||||
if (labelContent.isEmpty()) {
|
||||
if (!refClass.getTypeParameters().isEmpty() && targetSignature.contains("<")) {
|
||||
// If this is a generic type link try to use the TypeMirror representation.
|
||||
|
||||
// TODO:
|
||||
// TypeMirror refType = ch.getReferencedType(target);
|
||||
TypeMirror refType = target.asType();
|
||||
|
||||
if (refType != null) {
|
||||
return plainOrCode(isLinkPlain, getLink(
|
||||
new HtmlLinkInfo(configuration, HtmlLinkInfo.Kind.DEFAULT, refType)));
|
||||
}
|
||||
}
|
||||
labelContent = plainOrCode(isLinkPlain, Text.of(utils.getSimpleName(refClass)));
|
||||
}
|
||||
return getLink(new HtmlLinkInfo(configuration, HtmlLinkInfo.Kind.DEFAULT, refClass)
|
||||
.label(labelContent));
|
||||
} else if (refMem == null) {
|
||||
// Must be a member reference since refClass is not null and refMemName is not null.
|
||||
// However, refMem is null, so this referenced member does not exist.
|
||||
return labelContent;
|
||||
} else {
|
||||
// Must be a member reference since refClass is not null and refMemName is not null.
|
||||
// refMem is not null, so this @see tag must be referencing a valid member.
|
||||
TypeElement containing = utils.getEnclosingTypeElement(refMem);
|
||||
|
||||
// Find the enclosing type where the method is actually visible
|
||||
// in the inheritance hierarchy.
|
||||
ExecutableElement overriddenMethod = null;
|
||||
if (refMem.getKind() == ElementKind.METHOD) {
|
||||
VisibleMemberTable vmt = configuration.getVisibleMemberTable(containing);
|
||||
overriddenMethod = vmt.getOverriddenMethod((ExecutableElement)refMem);
|
||||
|
||||
if (overriddenMethod != null)
|
||||
containing = utils.getEnclosingTypeElement(overriddenMethod);
|
||||
}
|
||||
if (targetSignature.trim().startsWith("#") &&
|
||||
! (utils.isPublic(containing) || utils.isLinkable(containing))) {
|
||||
// Since the link is relative and the holder is not even being
|
||||
// documented, this must be an inherited link. Redirect it.
|
||||
// The current class either overrides the referenced member or
|
||||
// inherits it automatically.
|
||||
if (this instanceof ClassWriterImpl writer) {
|
||||
containing = writer.getTypeElement();
|
||||
} else if (!utils.isPublic(containing)) {
|
||||
// TODO:
|
||||
// messages.warning(
|
||||
// ch.getDocTreePath(see), "doclet.see.class_or_package_not_accessible",
|
||||
// tagName, utils.getFullyQualifiedName(containing));
|
||||
} else {
|
||||
// TODO:
|
||||
// messages.warning(
|
||||
// ch.getDocTreePath(see), "doclet.see.class_or_package_not_found",
|
||||
// tagName, seeText);
|
||||
}
|
||||
}
|
||||
if (configuration.currentTypeElement != containing) {
|
||||
refMemName = (utils.isConstructor(refMem))
|
||||
? refMemName
|
||||
: utils.getSimpleName(containing) + "." + refMemName;
|
||||
}
|
||||
if (utils.isExecutableElement(refMem)) {
|
||||
if (refMemName.indexOf('(') < 0) {
|
||||
refMemName += utils.makeSignature((ExecutableElement) refMem, null, true);
|
||||
}
|
||||
if (overriddenMethod != null) {
|
||||
// The method to actually link.
|
||||
refMem = overriddenMethod;
|
||||
}
|
||||
}
|
||||
|
||||
return getDocLink(HtmlLinkInfo.Kind.SEE_TAG, containing,
|
||||
refMem, (labelContent.isEmpty()
|
||||
? plainOrCode(isLinkPlain, Text.of(text))
|
||||
: labelContent), null, false);
|
||||
}
|
||||
}
|
||||
|
||||
private String removeTrailingSlash(String s) {
|
||||
return s.endsWith("/") ? s.substring(0, s.length() -1) : s;
|
||||
}
|
||||
|
||||
private Content plainOrCode(boolean plain, Content body) {
|
||||
return (plain || body.isEmpty()) ? body : HtmlTree.CODE(body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the inline comment.
|
||||
*
|
||||
@ -1660,8 +1350,8 @@ public class HtmlDocletWriter {
|
||||
}
|
||||
content.add(label);
|
||||
} else {
|
||||
Content c = seeTagToContent(element, node, context.within(node));
|
||||
content.add(c);
|
||||
TagletWriterImpl t = getTagletWriterInstance(context.within(node));
|
||||
content.add(t.linkTagOutput(element, node));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -1674,12 +1364,6 @@ public class HtmlDocletWriter {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitSee(SeeTree node, Content content) {
|
||||
content.add(seeTagToContent(element, node, context));
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitStartElement(StartElementTree node, Content content) {
|
||||
Content attrs = new ContentBuilder();
|
||||
|
@ -29,8 +29,10 @@ import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
@ -46,6 +48,7 @@ import javax.lang.model.util.SimpleElementVisitor14;
|
||||
import com.sun.source.doctree.DeprecatedTree;
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import com.sun.source.doctree.IndexTree;
|
||||
import com.sun.source.doctree.LinkTree;
|
||||
import com.sun.source.doctree.LiteralTree;
|
||||
import com.sun.source.doctree.ParamTree;
|
||||
import com.sun.source.doctree.ReturnTree;
|
||||
@ -66,6 +69,7 @@ import jdk.javadoc.internal.doclets.formats.html.markup.Text;
|
||||
import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
import jdk.javadoc.internal.doclets.toolkit.DocletElement;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Messages;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Resources;
|
||||
import jdk.javadoc.internal.doclets.toolkit.builders.SerializedFormBuilder;
|
||||
import jdk.javadoc.internal.doclets.toolkit.taglets.ParamTaglet;
|
||||
@ -79,6 +83,9 @@ import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.IndexItem;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.Utils;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.Utils.PreviewFlagProvider;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable;
|
||||
|
||||
import static com.sun.source.doctree.DocTree.Kind.LINK_PLAIN;
|
||||
|
||||
/**
|
||||
* The taglet writer that writes HTML.
|
||||
@ -143,6 +150,9 @@ public class TagletWriterImpl extends TagletWriter {
|
||||
private final HtmlOptions options;
|
||||
private final Utils utils;
|
||||
private final Resources resources;
|
||||
|
||||
private final Messages messages;
|
||||
|
||||
private final Contents contents;
|
||||
private final Context context;
|
||||
|
||||
@ -186,6 +196,7 @@ public class TagletWriterImpl extends TagletWriter {
|
||||
configuration = htmlWriter.configuration;
|
||||
options = configuration.getOptions();
|
||||
utils = configuration.utils;
|
||||
messages = configuration.messages;
|
||||
resources = configuration.getDocResources();
|
||||
contents = configuration.getContents();
|
||||
}
|
||||
@ -270,6 +281,32 @@ public class TagletWriterImpl extends TagletWriter {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content linkTagOutput(Element element, LinkTree tag) {
|
||||
CommentHelper ch = utils.getCommentHelper(element);
|
||||
|
||||
var linkRef = tag.getReference();
|
||||
if (linkRef == null) {
|
||||
messages.warning(ch.getDocTreePath(tag), "doclet.link.no_reference");
|
||||
return invalidTagOutput(resources.getText("doclet.tag.invalid_input", tag.toString()),
|
||||
Optional.empty());
|
||||
}
|
||||
|
||||
DocTree.Kind kind = tag.getKind();
|
||||
String tagName = ch.getTagName(tag);
|
||||
String refSignature = ch.getReferencedSignature(linkRef);
|
||||
|
||||
return linkSeeReferenceOutput(element,
|
||||
tag,
|
||||
refSignature,
|
||||
ch.getReferencedElement(tag),
|
||||
tagName,
|
||||
(kind == LINK_PLAIN),
|
||||
htmlWriter.commentTagsToContent(element, tag.getLabel(), context),
|
||||
(key, args) -> messages.warning(ch.getDocTreePath(tag), key, args)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Content literalTagOutput(Element element, LiteralTree tag) {
|
||||
return Text.of(utils.normalizeNewlines(tag.getBody().getBody()));
|
||||
@ -315,8 +352,9 @@ public class TagletWriterImpl extends TagletWriter {
|
||||
@Override
|
||||
public Content seeTagOutput(Element holder, List<? extends SeeTree> seeTags) {
|
||||
List<Content> links = new ArrayList<>();
|
||||
for (DocTree dt : seeTags) {
|
||||
links.add(htmlWriter.seeTagToContent(holder, dt, context.within(dt)));
|
||||
for (SeeTree dt : seeTags) {
|
||||
TagletWriterImpl t = new TagletWriterImpl(htmlWriter, context.within(dt));
|
||||
links.add(t.seeTagOutput(holder, dt));
|
||||
}
|
||||
if (utils.isVariableElement(holder) && ((VariableElement)holder).getConstantValue() != null &&
|
||||
htmlWriter instanceof ClassWriterImpl writer) {
|
||||
@ -363,6 +401,203 @@ public class TagletWriterImpl extends TagletWriter {
|
||||
return s.length() > SEE_TAG_MAX_INLINE_LENGTH || s.contains(",");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the output for a single {@code @see} tag}
|
||||
*
|
||||
* @param element the element that has the documentation comment containing this tag
|
||||
* @param seeTag the tag
|
||||
*/
|
||||
private Content seeTagOutput(Element element, SeeTree seeTag) {
|
||||
List<? extends DocTree> ref = seeTag.getReference();
|
||||
assert !ref.isEmpty();
|
||||
DocTree ref0 = ref.get(0);
|
||||
switch (ref0.getKind()) {
|
||||
case TEXT, START_ELEMENT -> {
|
||||
// @see "Reference"
|
||||
// @see <a href="...">...</a>
|
||||
return htmlWriter.commentTagsToContent(element, ref, false, false);
|
||||
}
|
||||
|
||||
case REFERENCE -> {
|
||||
// @see reference label...
|
||||
CommentHelper ch = utils.getCommentHelper(element);
|
||||
String tagName = ch.getTagName(seeTag);
|
||||
String refSignature = ch.getReferencedSignature(ref0);
|
||||
List<? extends DocTree> label = ref.subList(1, ref.size());
|
||||
|
||||
return linkSeeReferenceOutput(element,
|
||||
seeTag,
|
||||
refSignature,
|
||||
ch.getReferencedElement(seeTag),
|
||||
tagName,
|
||||
false,
|
||||
htmlWriter.commentTagsToContent(element, label, context),
|
||||
(key, args) -> messages.warning(ch.getDocTreePath(seeTag), key, args)
|
||||
);
|
||||
}
|
||||
|
||||
case ERRONEOUS -> {
|
||||
return invalidTagOutput(resources.getText("doclet.tag.invalid_input",
|
||||
ref0.toString()),
|
||||
Optional.empty());
|
||||
}
|
||||
|
||||
default -> throw new IllegalStateException(ref0.getKind().toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Worker method to generate a link from the information in different kinds of tags,
|
||||
* such as {@code {@link ...}} tags, {@code @see ...} tags and the {@code link} markup tag
|
||||
* in a {@code {@snippet ...}} tag.
|
||||
*
|
||||
* @param holder the element that has the documentation comment containing the information
|
||||
* @param refTree the tree node containing the information, or {@code null} if not available
|
||||
* @param refSignature the normalized signature of the target of the reference
|
||||
* @param ref the target of the reference
|
||||
* @param tagName the name of the tag in the source, to be used in diagnostics
|
||||
* @param isLinkPlain {@code true} if the link should be presented in "plain" font,
|
||||
* or {@code false} for "code" font
|
||||
* @param label the label for the link,
|
||||
* or an empty item to use a default label derived from the signature
|
||||
* @param reportWarning a function to report warnings about issues found in the reference
|
||||
*
|
||||
* @return the output containing the generated link, or content indicating an error
|
||||
*/
|
||||
private Content linkSeeReferenceOutput(Element holder,
|
||||
DocTree refTree,
|
||||
String refSignature,
|
||||
Element ref,
|
||||
String tagName,
|
||||
boolean isLinkPlain,
|
||||
Content label,
|
||||
BiConsumer<String, Object[]> reportWarning) {
|
||||
Content labelContent = plainOrCode(isLinkPlain, label);
|
||||
|
||||
// The signature from the @see tag. We will output this text when a label is not specified.
|
||||
Content text = plainOrCode(isLinkPlain,
|
||||
Text.of(Objects.requireNonNullElse(refSignature, "")));
|
||||
|
||||
CommentHelper ch = utils.getCommentHelper(holder);
|
||||
TypeElement refClass = ch.getReferencedClass(ref);
|
||||
Element refMem = ch.getReferencedMember(ref);
|
||||
String refMemName = ch.getReferencedMemberName(refSignature);
|
||||
|
||||
if (refMemName == null && refMem != null) {
|
||||
refMemName = refMem.toString();
|
||||
}
|
||||
if (refClass == null) {
|
||||
ModuleElement refModule = ch.getReferencedModule(ref);
|
||||
if (refModule != null && utils.isIncluded(refModule)) {
|
||||
return htmlWriter.getModuleLink(refModule, labelContent.isEmpty() ? text : labelContent);
|
||||
}
|
||||
//@see is not referencing an included class
|
||||
PackageElement refPackage = ch.getReferencedPackage(ref);
|
||||
if (refPackage != null && utils.isIncluded(refPackage)) {
|
||||
//@see is referencing an included package
|
||||
if (labelContent.isEmpty())
|
||||
labelContent = plainOrCode(isLinkPlain,
|
||||
Text.of(refPackage.getQualifiedName()));
|
||||
return htmlWriter.getPackageLink(refPackage, labelContent);
|
||||
} else {
|
||||
// @see is not referencing an included class, module or package. Check for cross links.
|
||||
String refModuleName = ch.getReferencedModuleName(refSignature);
|
||||
DocLink elementCrossLink = (refPackage != null) ? htmlWriter.getCrossPackageLink(refPackage) :
|
||||
(configuration.extern.isModule(refModuleName))
|
||||
? htmlWriter.getCrossModuleLink(utils.elementUtils.getModuleElement(refModuleName))
|
||||
: null;
|
||||
if (elementCrossLink != null) {
|
||||
// Element cross link found
|
||||
return htmlWriter.links.createExternalLink(elementCrossLink,
|
||||
(labelContent.isEmpty() ? text : labelContent));
|
||||
} else {
|
||||
// No cross link found so print warning
|
||||
if (!configuration.isDocLintReferenceGroupEnabled()) {
|
||||
reportWarning.accept(
|
||||
"doclet.see.class_or_package_not_found",
|
||||
new Object[] { "@" + tagName, refSignature});
|
||||
}
|
||||
return htmlWriter.invalidTagOutput(resources.getText("doclet.tag.invalid", tagName),
|
||||
Optional.of(labelContent.isEmpty() ? text: labelContent));
|
||||
}
|
||||
}
|
||||
} else if (refMemName == null) {
|
||||
// Must be a class reference since refClass is not null and refMemName is null.
|
||||
if (labelContent.isEmpty() && refTree != null) {
|
||||
TypeMirror referencedType = ch.getReferencedType(refTree);
|
||||
if (utils.isGenericType(referencedType)) {
|
||||
// This is a generic type link, use the TypeMirror representation.
|
||||
return plainOrCode(isLinkPlain, htmlWriter.getLink(
|
||||
new HtmlLinkInfo(configuration, HtmlLinkInfo.Kind.DEFAULT, referencedType)));
|
||||
}
|
||||
labelContent = plainOrCode(isLinkPlain, Text.of(utils.getSimpleName(refClass)));
|
||||
}
|
||||
return htmlWriter.getLink(new HtmlLinkInfo(configuration, HtmlLinkInfo.Kind.DEFAULT, refClass)
|
||||
.label(labelContent));
|
||||
} else if (refMem == null) {
|
||||
// Must be a member reference since refClass is not null and refMemName is not null.
|
||||
// However, refMem is null, so this referenced member does not exist.
|
||||
return (labelContent.isEmpty() ? text: labelContent);
|
||||
} else {
|
||||
// Must be a member reference since refClass is not null and refMemName is not null.
|
||||
// refMem is not null, so this @see tag must be referencing a valid member.
|
||||
TypeElement containing = utils.getEnclosingTypeElement(refMem);
|
||||
|
||||
// Find the enclosing type where the method is actually visible
|
||||
// in the inheritance hierarchy.
|
||||
ExecutableElement overriddenMethod = null;
|
||||
if (refMem.getKind() == ElementKind.METHOD) {
|
||||
VisibleMemberTable vmt = configuration.getVisibleMemberTable(containing);
|
||||
overriddenMethod = vmt.getOverriddenMethod((ExecutableElement)refMem);
|
||||
|
||||
if (overriddenMethod != null)
|
||||
containing = utils.getEnclosingTypeElement(overriddenMethod);
|
||||
}
|
||||
if (refSignature.trim().startsWith("#") &&
|
||||
! (utils.isPublic(containing) || utils.isLinkable(containing))) {
|
||||
// Since the link is relative and the holder is not even being
|
||||
// documented, this must be an inherited link. Redirect it.
|
||||
// The current class either overrides the referenced member or
|
||||
// inherits it automatically.
|
||||
if (htmlWriter instanceof ClassWriterImpl writer) {
|
||||
containing = writer.getTypeElement();
|
||||
} else if (!utils.isPublic(containing)) {
|
||||
reportWarning.accept("doclet.see.class_or_package_not_accessible",
|
||||
new Object[] { tagName, utils.getFullyQualifiedName(containing)});
|
||||
} else {
|
||||
if (!configuration.isDocLintReferenceGroupEnabled()) {
|
||||
reportWarning.accept("doclet.see.class_or_package_not_found",
|
||||
new Object[] { tagName, refSignature });
|
||||
}
|
||||
}
|
||||
}
|
||||
if (configuration.currentTypeElement != containing) {
|
||||
refMemName = (utils.isConstructor(refMem))
|
||||
? refMemName
|
||||
: utils.getSimpleName(containing) + "." + refMemName;
|
||||
}
|
||||
if (utils.isExecutableElement(refMem)) {
|
||||
if (refMemName.indexOf('(') < 0) {
|
||||
refMemName += utils.makeSignature((ExecutableElement) refMem, null, true);
|
||||
}
|
||||
if (overriddenMethod != null) {
|
||||
// The method to actually link.
|
||||
refMem = overriddenMethod;
|
||||
}
|
||||
}
|
||||
|
||||
return htmlWriter.getDocLink(HtmlLinkInfo.Kind.SEE_TAG, containing,
|
||||
refMem, (labelContent.isEmpty()
|
||||
? plainOrCode(isLinkPlain, Text.of(refMemName))
|
||||
: labelContent), null, false);
|
||||
}
|
||||
}
|
||||
|
||||
private Content plainOrCode(boolean plain, Content body) {
|
||||
return (plain || body.isEmpty()) ? body : HtmlTree.CODE(body);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content simpleBlockTagOutput(Element element, List<? extends DocTree> simpleTags, String header) {
|
||||
CommentHelper ch = utils.getCommentHelper(element);
|
||||
@ -434,7 +669,14 @@ public class TagletWriterImpl extends TagletWriter {
|
||||
//disable preview tagging inside the snippets:
|
||||
PreviewFlagProvider prevPreviewProvider = utils.setPreviewFlagProvider(el -> false);
|
||||
try {
|
||||
c = new ContentBuilder(htmlWriter.linkToContent(element, e, t, sequence.toString()));
|
||||
c = linkSeeReferenceOutput(element,
|
||||
null,
|
||||
t,
|
||||
e,
|
||||
"link",
|
||||
false, // TODO: for now
|
||||
Text.of(sequence.toString()),
|
||||
(key, args) -> { /* TODO: report diagnostic */ });
|
||||
} finally {
|
||||
utils.setPreviewFlagProvider(prevPreviewProvider);
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ import javax.lang.model.type.TypeMirror;
|
||||
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import com.sun.source.doctree.IndexTree;
|
||||
import com.sun.source.doctree.LinkTree;
|
||||
import com.sun.source.doctree.LiteralTree;
|
||||
import com.sun.source.doctree.ParamTree;
|
||||
import com.sun.source.doctree.ReturnTree;
|
||||
@ -107,6 +108,16 @@ public abstract class TagletWriter {
|
||||
*/
|
||||
protected abstract Content deprecatedTagOutput(Element element);
|
||||
|
||||
/**
|
||||
* Returns the output for a {@code {@link ...}} or {@code {@linkplain ...}} tag.
|
||||
*
|
||||
* @param element The element that owns the doc comment
|
||||
* @param tag the tag
|
||||
*
|
||||
* @return the output
|
||||
*/
|
||||
protected abstract Content linkTagOutput(Element element, LinkTree tag);
|
||||
|
||||
/**
|
||||
* Returns the output for a {@code {@literal ...}} tag.
|
||||
*
|
||||
|
@ -156,8 +156,8 @@ public class CommentHelper {
|
||||
public TypeMirror getType(ReferenceTree rtree) {
|
||||
DocTreePath docTreePath = getDocTreePath(rtree);
|
||||
if (docTreePath != null) {
|
||||
DocTrees doctrees = configuration.docEnv.getDocTrees();
|
||||
return doctrees.getType(docTreePath);
|
||||
DocTrees docTrees = configuration.docEnv.getDocTrees();
|
||||
return docTrees.getType(docTreePath);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -213,11 +213,6 @@ public class CommentHelper {
|
||||
return (utils.isExecutableElement(e) || utils.isVariableElement(e)) ? e : null;
|
||||
}
|
||||
|
||||
public String getReferencedMemberName(DocTree dtree) {
|
||||
String s = getReferencedSignature(dtree);
|
||||
return getReferencedMemberName(s);
|
||||
}
|
||||
|
||||
public String getReferencedMemberName(String signature) {
|
||||
if (signature == null) {
|
||||
return null;
|
||||
@ -259,7 +254,7 @@ public class CommentHelper {
|
||||
return getFirstSentenceTrees(getBody(dtree));
|
||||
}
|
||||
|
||||
private Element getReferencedElement(DocTree dtree) {
|
||||
public Element getReferencedElement(DocTree dtree) {
|
||||
return new ReferenceDocTreeVisitor<Element>() {
|
||||
@Override
|
||||
public Element visitReference(ReferenceTree node, Void p) {
|
||||
@ -286,7 +281,10 @@ public class CommentHelper {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getReferencedSignature(DocTree dtree) {
|
||||
/**
|
||||
* {@return the normalized signature from a {@code ReferenceTree}}
|
||||
*/
|
||||
public String getReferencedSignature(DocTree dtree) {
|
||||
return new ReferenceDocTreeVisitor<String>() {
|
||||
@Override
|
||||
public String visitReference(ReferenceTree node, Void p) {
|
||||
|
@ -66,7 +66,7 @@ public class TypeParameters<E> implements SubInterface<E> {
|
||||
*
|
||||
* @param <A> This is the first type parameter.
|
||||
*/
|
||||
public <A> void methodThatHasTypeParmaters(A... a) {}
|
||||
public <A> void methodThatHasTypeParameters(A... a) {}
|
||||
|
||||
/**
|
||||
* This method returns a TypeParameter array and takes in a TypeParameter array
|
||||
|
Loading…
Reference in New Issue
Block a user