8309566: Migrate away from TagletWriter and TagletWriterImpl
8311974: Clean up Utils.getBlockTags Reviewed-by: prappo
This commit is contained in:
parent
fb90af881b
commit
e51472e9a8
@ -46,7 +46,6 @@ import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
import jdk.javadoc.internal.doclets.toolkit.MemberSummaryWriter;
|
||||
import jdk.javadoc.internal.doclets.toolkit.MemberWriter;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Resources;
|
||||
import jdk.javadoc.internal.doclets.toolkit.taglets.DeprecatedTaglet;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.Utils;
|
||||
|
||||
/**
|
||||
@ -257,8 +256,8 @@ public abstract class AbstractMemberWriter implements MemberSummaryWriter, Membe
|
||||
* @param target the content to which the deprecated information will be added.
|
||||
*/
|
||||
protected void addDeprecatedInfo(Element member, Content target) {
|
||||
Content output = (new DeprecatedTaglet()).getAllBlockTagOutput(member,
|
||||
writer.getTagletWriterInstance(false));
|
||||
var t = configuration.tagletManager.getTaglet(DocTree.Kind.DEPRECATED);
|
||||
Content output = t.getAllBlockTagOutput(member, writer.getTagletWriterInstance(false));
|
||||
if (!output.isEmpty()) {
|
||||
target.add(HtmlTree.DIV(HtmlStyle.deprecationBlock, output));
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ModuleElement;
|
||||
@ -41,6 +42,7 @@ import javax.lang.model.util.SimpleElementVisitor8;
|
||||
|
||||
import com.sun.source.doctree.DeprecatedTree;
|
||||
import com.sun.source.doctree.DocTree;
|
||||
|
||||
import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.Entity;
|
||||
@ -51,7 +53,6 @@ import jdk.javadoc.internal.doclets.formats.html.markup.TagName;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.Text;
|
||||
import jdk.javadoc.internal.doclets.toolkit.ClassWriter;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
import jdk.javadoc.internal.doclets.toolkit.taglets.ParamTaglet;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.ClassTree;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
|
||||
@ -92,6 +93,11 @@ public class ClassWriterImpl extends SubWriterHolderWriter implements ClassWrite
|
||||
this.classTree = classTree;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getOutputInstance() {
|
||||
return new ContentBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getHeader(String header) {
|
||||
HtmlTree body = getBody(getWindowTitle(utils.getSimpleName(typeElement)));
|
||||
@ -173,7 +179,7 @@ public class ClassWriterImpl extends SubWriterHolderWriter implements ClassWrite
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TypeElement getCurrentPageElement() {
|
||||
public TypeElement getCurrentPageElement() {
|
||||
return typeElement;
|
||||
}
|
||||
|
||||
@ -268,8 +274,8 @@ public class ClassWriterImpl extends SubWriterHolderWriter implements ClassWrite
|
||||
@Override
|
||||
public void addParamInfo(Content target) {
|
||||
if (utils.hasBlockTag(typeElement, DocTree.Kind.PARAM)) {
|
||||
Content paramInfo = (new ParamTaglet()).getAllBlockTagOutput(typeElement,
|
||||
getTagletWriterInstance(false));
|
||||
var t = configuration.tagletManager.getTaglet(DocTree.Kind.PARAM);
|
||||
Content paramInfo = t.getAllBlockTagOutput(typeElement, getTagletWriterInstance(false));
|
||||
if (!paramInfo.isEmpty()) {
|
||||
target.add(HtmlTree.DL(HtmlStyle.notes, paramInfo));
|
||||
}
|
||||
|
@ -25,8 +25,13 @@
|
||||
|
||||
package jdk.javadoc.internal.doclets.formats.html;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@ -39,6 +44,7 @@ import java.util.stream.Collectors;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.PackageElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.tools.DocumentationTool;
|
||||
import javax.tools.JavaFileManager;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.StandardJavaFileManager;
|
||||
@ -49,6 +55,7 @@ import jdk.javadoc.doclet.Reporter;
|
||||
import jdk.javadoc.doclet.StandardDoclet;
|
||||
import jdk.javadoc.doclet.Taglet;
|
||||
import jdk.javadoc.internal.Versions;
|
||||
import jdk.javadoc.internal.doclets.formats.html.taglets.TagletManager;
|
||||
import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
|
||||
import jdk.javadoc.internal.doclets.toolkit.BaseOptions;
|
||||
import jdk.javadoc.internal.doclets.toolkit.DocletException;
|
||||
@ -61,6 +68,7 @@ import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.NewAPIBuilder;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.PreviewAPIListBuilder;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.SimpleDocletException;
|
||||
|
||||
/**
|
||||
* Configure the output based on the command-line options.
|
||||
@ -104,7 +112,7 @@ public class HtmlConfiguration extends BaseConfiguration {
|
||||
* 2. items for elements are added in bulk before generating the index files
|
||||
* 3. additional items are added as needed
|
||||
*/
|
||||
protected HtmlIndexBuilder mainIndex;
|
||||
public HtmlIndexBuilder mainIndex;
|
||||
|
||||
/**
|
||||
* The collection of deprecated items, if any, to be displayed on the deprecated-list page,
|
||||
@ -133,7 +141,7 @@ public class HtmlConfiguration extends BaseConfiguration {
|
||||
|
||||
public Contents contents;
|
||||
|
||||
protected final Messages messages;
|
||||
public final Messages messages;
|
||||
|
||||
public DocPaths docPaths;
|
||||
|
||||
@ -143,6 +151,11 @@ public class HtmlConfiguration extends BaseConfiguration {
|
||||
|
||||
private final HtmlOptions options;
|
||||
|
||||
/**
|
||||
* The taglet manager.
|
||||
*/
|
||||
public TagletManager tagletManager;
|
||||
|
||||
/**
|
||||
* Kinds of conditional pages.
|
||||
*/
|
||||
@ -424,6 +437,125 @@ public class HtmlConfiguration extends BaseConfiguration {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
String snippetPath = options.snippetPath();
|
||||
if (snippetPath != null) {
|
||||
Messages messages = getMessages();
|
||||
JavaFileManager fm = getFileManager();
|
||||
if (fm instanceof StandardJavaFileManager) {
|
||||
try {
|
||||
List<Path> sp = Arrays.stream(snippetPath.split(File.pathSeparator))
|
||||
.map(Path::of)
|
||||
.toList();
|
||||
StandardJavaFileManager sfm = (StandardJavaFileManager) fm;
|
||||
sfm.setLocationFromPaths(DocumentationTool.Location.SNIPPET_PATH, sp);
|
||||
} catch (IOException | InvalidPathException e) {
|
||||
throw new SimpleDocletException(messages.getResources().getText(
|
||||
"doclet.error_setting_snippet_path", snippetPath, e), e);
|
||||
}
|
||||
} else {
|
||||
throw new SimpleDocletException(messages.getResources().getText(
|
||||
"doclet.cannot_use_snippet_path", snippetPath));
|
||||
}
|
||||
}
|
||||
|
||||
initTagletManager(options.customTagStrs());
|
||||
|
||||
return super.finishOptionSettings0();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the taglet manager. The strings to initialize the simple custom tags should
|
||||
* be in the following format: "[tag name]:[location str]:[heading]".
|
||||
*
|
||||
* @param customTagStrs the set two-dimensional arrays of strings. These arrays contain
|
||||
* either -tag or -taglet arguments.
|
||||
*/
|
||||
private void initTagletManager(Set<List<String>> customTagStrs) {
|
||||
tagletManager = tagletManager != null ? tagletManager : new TagletManager(this);
|
||||
JavaFileManager fileManager = getFileManager();
|
||||
Messages messages = getMessages();
|
||||
try {
|
||||
tagletManager.initTagletPath(fileManager);
|
||||
tagletManager.loadTaglets(fileManager);
|
||||
|
||||
for (List<String> args : customTagStrs) {
|
||||
if (args.get(0).equals("-taglet")) {
|
||||
tagletManager.addCustomTag(args.get(1), fileManager);
|
||||
continue;
|
||||
}
|
||||
/* Since there are few constraints on the characters in a tag name,
|
||||
* and real world examples with ':' in the tag name, we cannot simply use
|
||||
* String.split(regex); instead, we tokenize the string, allowing
|
||||
* special characters to be escaped with '\'. */
|
||||
List<String> tokens = tokenize(args.get(1), 3);
|
||||
switch (tokens.size()) {
|
||||
case 1 -> {
|
||||
String tagName = args.get(1);
|
||||
if (tagletManager.isKnownCustomTag(tagName)) {
|
||||
//reorder a standard tag
|
||||
tagletManager.addNewSimpleCustomTag(tagName, null, "");
|
||||
} else {
|
||||
//Create a simple tag with the heading that has the same name as the tag.
|
||||
StringBuilder heading = new StringBuilder(tagName + ":");
|
||||
heading.setCharAt(0, Character.toUpperCase(tagName.charAt(0)));
|
||||
tagletManager.addNewSimpleCustomTag(tagName, heading.toString(), "a");
|
||||
}
|
||||
}
|
||||
|
||||
case 2 ->
|
||||
//Add simple taglet without heading, probably to excluding it in the output.
|
||||
tagletManager.addNewSimpleCustomTag(tokens.get(0), tokens.get(1), "");
|
||||
|
||||
case 3 ->
|
||||
tagletManager.addNewSimpleCustomTag(tokens.get(0), tokens.get(2), tokens.get(1));
|
||||
|
||||
default ->
|
||||
messages.error("doclet.Error_invalid_custom_tag_argument", args.get(1));
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
messages.error("doclet.taglet_could_not_set_location", e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a string, return an array of tokens, separated by ':'.
|
||||
* The separator character can be escaped with the '\' character.
|
||||
* The '\' character may also be escaped with the '\' character.
|
||||
*
|
||||
* @param s the string to tokenize
|
||||
* @param maxTokens the maximum number of tokens returned. If the
|
||||
* max is reached, the remaining part of s is appended
|
||||
* to the end of the last token.
|
||||
* @return an array of tokens
|
||||
*/
|
||||
private List<String> tokenize(String s, int maxTokens) {
|
||||
List<String> tokens = new ArrayList<>();
|
||||
StringBuilder token = new StringBuilder();
|
||||
boolean prevIsEscapeChar = false;
|
||||
for (int i = 0; i < s.length(); i += Character.charCount(i)) {
|
||||
int currentChar = s.codePointAt(i);
|
||||
if (prevIsEscapeChar) {
|
||||
// Case 1: escaped character
|
||||
token.appendCodePoint(currentChar);
|
||||
prevIsEscapeChar = false;
|
||||
} else if (currentChar == ':' && tokens.size() < maxTokens - 1) {
|
||||
// Case 2: separator
|
||||
tokens.add(token.toString());
|
||||
token = new StringBuilder();
|
||||
} else if (currentChar == '\\') {
|
||||
// Case 3: escape character
|
||||
prevIsEscapeChar = true;
|
||||
} else {
|
||||
// Case 4: regular character
|
||||
token.appendCodePoint(currentChar);
|
||||
}
|
||||
}
|
||||
if (token.length() > 0) {
|
||||
tokens.add(token.toString());
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -319,6 +319,16 @@ public class HtmlDoclet extends AbstractDoclet {
|
||||
copyLegalFiles(options.createIndex());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void generateFiles() throws DocletException {
|
||||
super.generateFiles();
|
||||
|
||||
if (configuration.tagletManager != null) { // may be null, if no files generated, perhaps because of errros
|
||||
configuration.tagletManager.printReport();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void copyJqueryFiles() throws DocletException {
|
||||
List<String> files = Arrays.asList(
|
||||
DocPaths.JQUERY_JS.getPath(),
|
||||
|
@ -41,6 +41,7 @@ import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.AnnotationValue;
|
||||
import javax.lang.model.element.Element;
|
||||
@ -68,13 +69,11 @@ import com.sun.source.doctree.EndElementTree;
|
||||
import com.sun.source.doctree.EntityTree;
|
||||
import com.sun.source.doctree.ErroneousTree;
|
||||
import com.sun.source.doctree.EscapeTree;
|
||||
import com.sun.source.doctree.IndexTree;
|
||||
import com.sun.source.doctree.InheritDocTree;
|
||||
import com.sun.source.doctree.InlineTagTree;
|
||||
import com.sun.source.doctree.LinkTree;
|
||||
import com.sun.source.doctree.LiteralTree;
|
||||
import com.sun.source.doctree.StartElementTree;
|
||||
import com.sun.source.doctree.SummaryTree;
|
||||
import com.sun.source.doctree.SystemPropertyTree;
|
||||
import com.sun.source.doctree.TextTree;
|
||||
import com.sun.source.util.DocTreePath;
|
||||
import com.sun.source.util.SimpleDocTreeVisitor;
|
||||
@ -91,12 +90,11 @@ import jdk.javadoc.internal.doclets.formats.html.markup.RawHtml;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.Script;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.TagName;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.Text;
|
||||
import jdk.javadoc.internal.doclets.formats.html.taglets.Taglet;
|
||||
import jdk.javadoc.internal.doclets.formats.html.taglets.TagletWriter;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Messages;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Resources;
|
||||
import jdk.javadoc.internal.doclets.toolkit.taglets.DocRootTaglet;
|
||||
import jdk.javadoc.internal.doclets.toolkit.taglets.Taglet;
|
||||
import jdk.javadoc.internal.doclets.toolkit.taglets.TagletWriter;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.Comparators;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocFile;
|
||||
@ -109,14 +107,9 @@ import jdk.javadoc.internal.doclets.toolkit.util.Utils;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.Utils.DeclarationPreviewLanguageFeatures;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.Utils.ElementFlag;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.Utils.PreviewSummary;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable;
|
||||
import jdk.javadoc.internal.doclint.HtmlTag;
|
||||
|
||||
import static com.sun.source.doctree.DocTree.Kind.CODE;
|
||||
import static com.sun.source.doctree.DocTree.Kind.COMMENT;
|
||||
import static com.sun.source.doctree.DocTree.Kind.LINK;
|
||||
import static com.sun.source.doctree.DocTree.Kind.LINK_PLAIN;
|
||||
import static com.sun.source.doctree.DocTree.Kind.SEE;
|
||||
import static com.sun.source.doctree.DocTree.Kind.TEXT;
|
||||
|
||||
|
||||
@ -160,11 +153,11 @@ public class HtmlDocletWriter {
|
||||
|
||||
protected final Contents contents;
|
||||
|
||||
protected final Messages messages;
|
||||
public final Messages messages;
|
||||
|
||||
protected final Resources resources;
|
||||
|
||||
protected final Links links;
|
||||
public final Links links;
|
||||
|
||||
protected final DocPaths docPaths;
|
||||
|
||||
@ -197,7 +190,7 @@ public class HtmlDocletWriter {
|
||||
* (Ideally, javadoc should be tracking all id's generated in a file
|
||||
* to avoid generating duplicates.)
|
||||
*/
|
||||
Map<String, Integer> indexAnchorTable = new HashMap<>();
|
||||
public final Map<String, Integer> indexAnchorTable = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Creates an {@code HtmlDocletWriter}.
|
||||
@ -278,7 +271,7 @@ public class HtmlDocletWriter {
|
||||
return buf.toString();
|
||||
}
|
||||
//where:
|
||||
// Note: {@docRoot} is not case sensitive when passed in with a command-line option:
|
||||
// Note: {@docRoot} is not case-sensitive when passed in with a command-line option:
|
||||
private static final Pattern docrootPattern =
|
||||
Pattern.compile(Pattern.quote("{@docroot}"), Pattern.CASE_INSENSITIVE);
|
||||
|
||||
@ -374,9 +367,8 @@ public class HtmlDocletWriter {
|
||||
return !output.isEmpty();
|
||||
}
|
||||
|
||||
private Content getInlineTagOutput(Element element, DocTree tree, TagletWriterImpl.Context context) {
|
||||
return getTagletWriterInstance(context)
|
||||
.getInlineTagOutput(element, configuration.tagletManager, tree);
|
||||
private Content getInlineTagOutput(Element element, InlineTagTree tree, TagletWriter.Context context) {
|
||||
return getTagletWriterInstance(context).getInlineTagOutput(element, tree);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -386,7 +378,7 @@ public class HtmlDocletWriter {
|
||||
* @return a TagletWriter that knows how to write HTML.
|
||||
*/
|
||||
public TagletWriter getTagletWriterInstance(boolean isFirstSentence) {
|
||||
return new TagletWriterImpl(this, isFirstSentence);
|
||||
return new TagletWriter(this, isFirstSentence);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -395,8 +387,8 @@ public class HtmlDocletWriter {
|
||||
* @param context the enclosing context
|
||||
* @return a TagletWriter
|
||||
*/
|
||||
public TagletWriterImpl getTagletWriterInstance(TagletWriterImpl.Context context) {
|
||||
return new TagletWriterImpl(this, context);
|
||||
public TagletWriter getTagletWriterInstance(TagletWriter.Context context) {
|
||||
return new TagletWriter(this, context);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -756,7 +748,7 @@ public class HtmlDocletWriter {
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
* Return a class cross link to external class documentation.
|
||||
* Return a class cross-link to external class documentation.
|
||||
* The -link option does not allow users to
|
||||
* link to external classes in the "default" package.
|
||||
*
|
||||
@ -886,7 +878,7 @@ public class HtmlDocletWriter {
|
||||
*
|
||||
* @return the type element of the current page.
|
||||
*/
|
||||
protected TypeElement getCurrentPageElement() {
|
||||
public TypeElement getCurrentPageElement() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -1177,7 +1169,7 @@ public class HtmlDocletWriter {
|
||||
boolean isFirstSentence,
|
||||
boolean inSummary) {
|
||||
return commentTagsToContent(element, trees,
|
||||
new TagletWriterImpl.Context(isFirstSentence, inSummary));
|
||||
new TagletWriter.Context(isFirstSentence, inSummary));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1194,7 +1186,7 @@ public class HtmlDocletWriter {
|
||||
*/
|
||||
public Content commentTagsToContent(Element element,
|
||||
List<? extends DocTree> trees,
|
||||
TagletWriterImpl.Context context)
|
||||
TagletWriter.Context context)
|
||||
{
|
||||
final Content result = new ContentBuilder() {
|
||||
@Override
|
||||
@ -1307,12 +1299,6 @@ public class HtmlDocletWriter {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitDocRoot(DocRootTree node, Content content) {
|
||||
content.add(getInlineTagOutput(element, node, context));
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitEndElement(EndElementTree node, Content content) {
|
||||
content.add(RawHtml.endElement(node.getName()));
|
||||
@ -1361,43 +1347,6 @@ public class HtmlDocletWriter {
|
||||
return (context.isFirstSentence && !output.isEmpty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitIndex(IndexTree node, Content content) {
|
||||
Content output = getInlineTagOutput(element, node, context);
|
||||
if (output != null) {
|
||||
content.add(output);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitLink(LinkTree node, Content content) {
|
||||
var inTags = context.inTags;
|
||||
if (inTags.contains(LINK) || inTags.contains(LINK_PLAIN) || inTags.contains(SEE)) {
|
||||
DocTreePath dtp = ch.getDocTreePath(node);
|
||||
if (dtp != null) {
|
||||
messages.warning(dtp, "doclet.see.nested_link", "{@" + node.getTagName() + "}");
|
||||
}
|
||||
Content label = commentTagsToContent(element, node.getLabel(), context);
|
||||
if (label.isEmpty()) {
|
||||
label = Text.of(node.getReference().getSignature());
|
||||
}
|
||||
content.add(label);
|
||||
} else {
|
||||
TagletWriterImpl t = getTagletWriterInstance(context.within(node));
|
||||
content.add(t.linkTagOutput(element, node));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitLiteral(LiteralTree node, Content content) {
|
||||
String s = node.getBody().getBody();
|
||||
Content t = Text.of(Text.normalizeNewlines(s));
|
||||
content.add(node.getKind() == CODE ? HtmlTree.CODE(t) : t);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitStartElement(StartElementTree node, Content content) {
|
||||
Content attrs = new ContentBuilder();
|
||||
@ -1411,22 +1360,6 @@ public class HtmlDocletWriter {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitSummary(SummaryTree node, Content content) {
|
||||
Content output = getInlineTagOutput(element, node, context);
|
||||
content.add(output);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitSystemProperty(SystemPropertyTree node, Content content) {
|
||||
Content output = getInlineTagOutput(element, node, context);
|
||||
if (output != null) {
|
||||
content.add(output);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private CharSequence textCleanup(String text, boolean isLast) {
|
||||
return textCleanup(text, isLast, false);
|
||||
}
|
||||
@ -1455,9 +1388,11 @@ public class HtmlDocletWriter {
|
||||
|
||||
@Override
|
||||
protected Boolean defaultAction(DocTree node, Content content) {
|
||||
Content output = getInlineTagOutput(element, node, context);
|
||||
if (output != null) {
|
||||
content.add(output);
|
||||
if (node instanceof InlineTagTree itt) {
|
||||
var output = getInlineTagOutput(element, itt, context);
|
||||
if (output != null) {
|
||||
content.add(output);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -1485,7 +1420,7 @@ public class HtmlDocletWriter {
|
||||
}
|
||||
|
||||
private void createSectionIdAndIndex(StartElementTree node, List<? extends DocTree> trees, Content attrs,
|
||||
Element element, TagletWriterImpl.Context context) {
|
||||
Element element, TagletWriter.Context context) {
|
||||
// Use existing id attribute if available
|
||||
String id = getIdAttributeValue(node).orElse(null);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
@ -1562,7 +1497,7 @@ public class HtmlDocletWriter {
|
||||
* @param detail the optional detail message which may contain preformatted text
|
||||
* @return the output
|
||||
*/
|
||||
protected Content invalidTagOutput(String summary, Optional<Content> detail) {
|
||||
public Content invalidTagOutput(String summary, Optional<Content> detail) {
|
||||
if (detail.isEmpty() || detail.get().isEmpty()) {
|
||||
return HtmlTree.SPAN(HtmlStyle.invalidTag, Text.of(summary));
|
||||
}
|
||||
@ -1665,7 +1600,7 @@ public class HtmlDocletWriter {
|
||||
}
|
||||
}.visit(element);
|
||||
if (redirectPathFromRoot != null) {
|
||||
text = "{@" + (new DocRootTaglet()).getName() + "}/"
|
||||
text = "{@" + Kind.DOC_ROOT.tagName + "}/"
|
||||
+ redirectPathFromRoot.resolve(text).getPath();
|
||||
return replaceDocRootDir(text);
|
||||
}
|
||||
@ -2049,7 +1984,7 @@ public class HtmlDocletWriter {
|
||||
* Returns the path of module/package specific stylesheets for the element.
|
||||
* @param element module/Package element
|
||||
* @return list of path of module/package specific stylesheets
|
||||
* @throws DocFileIOException
|
||||
* @throws DocFileIOException if an issue arises while accessing any stylesheets
|
||||
*/
|
||||
List<DocPath> getLocalStylesheets(Element element) throws DocFileIOException {
|
||||
List<DocPath> stylesheets = new ArrayList<>();
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -394,7 +394,7 @@ public class HtmlIds {
|
||||
*
|
||||
* @return the id
|
||||
*/
|
||||
static HtmlId forParam(String paramName) {
|
||||
public static HtmlId forParam(String paramName) {
|
||||
return HtmlId.of("param-" + paramName);
|
||||
}
|
||||
|
||||
@ -407,7 +407,7 @@ public class HtmlIds {
|
||||
*
|
||||
* @return the id
|
||||
*/
|
||||
static HtmlId forText(String text, Map<String, Integer> counts) {
|
||||
public static HtmlId forText(String text, Map<String, Integer> counts) {
|
||||
String base = text.replaceAll("\\s+", "");
|
||||
int count = counts.compute(base, (k, v) -> v == null ? 0 : v + 1);
|
||||
return HtmlId.of(count == 0 ? base : base + "-" + count);
|
||||
|
@ -28,6 +28,7 @@ package jdk.javadoc.internal.doclets.formats.html;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
@ -100,6 +101,11 @@ public class HtmlOptions extends BaseOptions {
|
||||
*/
|
||||
private boolean createTree = true;
|
||||
|
||||
/**
|
||||
* Arguments for command-line option {@code -tag} and {@code -taglet}.
|
||||
*/
|
||||
private final LinkedHashSet<List<String>> customTagStrs = new LinkedHashSet<>();
|
||||
|
||||
/**
|
||||
* Arguments for command-line option {@code -Xdoclint} and friends.
|
||||
* Collected set of doclint options.
|
||||
@ -175,6 +181,12 @@ public class HtmlOptions extends BaseOptions {
|
||||
*/
|
||||
private String packagesHeader = "";
|
||||
|
||||
/**
|
||||
* Argument for command-line option {@code --snippet-path}.
|
||||
* The path for external snippets.
|
||||
*/
|
||||
private String snippetPath = null;
|
||||
|
||||
/**
|
||||
* Argument for command-line option {@code -splitindex}.
|
||||
* True if command-line option "-splitindex" is used. Default value is
|
||||
@ -182,11 +194,23 @@ public class HtmlOptions extends BaseOptions {
|
||||
*/
|
||||
private boolean splitIndex = false;
|
||||
|
||||
/**
|
||||
* Argument for command-line option {@code --show-taglets}.
|
||||
* Show taglets (internal debug switch)
|
||||
*/
|
||||
private boolean showTaglets = false;
|
||||
|
||||
/**
|
||||
* Argument for command-line option {@code -stylesheetfile}.
|
||||
*/
|
||||
private String stylesheetFile = "";
|
||||
|
||||
/**
|
||||
* Argument for command-line option {@code -tagletpath}.
|
||||
* The path to Taglets
|
||||
*/
|
||||
private String tagletPath = null;
|
||||
|
||||
/**
|
||||
* Argument for command-line option {@code -top}.
|
||||
*/
|
||||
@ -406,6 +430,44 @@ public class HtmlOptions extends BaseOptions {
|
||||
}
|
||||
},
|
||||
|
||||
new Option(resources, "--snippet-path", 1) {
|
||||
@Override
|
||||
public boolean process(String opt, List<String> args) {
|
||||
snippetPath = args.get(0);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
new Option(resources, "-tag", 1) {
|
||||
@Override
|
||||
public boolean process(String opt, List<String> args) {
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
list.add(opt);
|
||||
list.add(args.get(0));
|
||||
customTagStrs.add(list);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
new Option(resources, "-taglet", 1) {
|
||||
@Override
|
||||
public boolean process(String opt, List<String> args) {
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
list.add(opt);
|
||||
list.add(args.get(0));
|
||||
customTagStrs.add(list);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
new Option(resources, "-tagletpath", 1) {
|
||||
@Override
|
||||
public boolean process(String opt, List<String> args) {
|
||||
tagletPath = args.get(0);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
new Option(resources, "-top", 1) {
|
||||
@Override
|
||||
public boolean process(String opt, List<String> args) {
|
||||
@ -489,6 +551,14 @@ public class HtmlOptions extends BaseOptions {
|
||||
messages.warning("doclet.NoFrames_specified");
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
new Hidden(resources, "--show-taglets") {
|
||||
@Override
|
||||
public boolean process(String opt, List<String> args) {
|
||||
showTaglets = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
);
|
||||
Set<BaseOptions.Option> allOptions = new TreeSet<>();
|
||||
@ -620,6 +690,13 @@ public class HtmlOptions extends BaseOptions {
|
||||
return createTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Arguments for command-line option {@code -tag} and {@code -taglet}.
|
||||
*/
|
||||
LinkedHashSet<List<String>> customTagStrs() {
|
||||
return customTagStrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Arguments for command-line option {@code -Xdoclint} and friends.
|
||||
* Collected set of doclint options.
|
||||
@ -721,6 +798,22 @@ public class HtmlOptions extends BaseOptions {
|
||||
return packagesHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Argument for command-line option {@code --show-taglets}.
|
||||
* Show taglets (internal debug switch)
|
||||
*/
|
||||
public boolean showTaglets() {
|
||||
return showTaglets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Argument for command-line option {@code --snippet-path}.
|
||||
* The path for external snippets.
|
||||
*/
|
||||
public String snippetPath() {
|
||||
return snippetPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Argument for command-line option {@code -splitindex}.
|
||||
* True if command-line option "-splitindex" is used. Default value is
|
||||
@ -737,6 +830,14 @@ public class HtmlOptions extends BaseOptions {
|
||||
return stylesheetFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Argument for command-line option {@code -tagletpath}.
|
||||
* The path to Taglets
|
||||
*/
|
||||
public String tagletPath() {
|
||||
return tagletPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Argument for command-line option {@code -top}.
|
||||
*/
|
||||
|
@ -25,20 +25,22 @@
|
||||
|
||||
package jdk.javadoc.internal.doclets.formats.html;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.SortedSet;
|
||||
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
import com.sun.source.doctree.DocTree;
|
||||
|
||||
import com.sun.source.doctree.SerialFieldTree;
|
||||
import com.sun.source.doctree.SerialTree;
|
||||
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.TagName;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.TagName;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.Text;
|
||||
import jdk.javadoc.internal.doclets.formats.html.taglets.TagletWriter;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
import jdk.javadoc.internal.doclets.toolkit.SerializedFormWriter;
|
||||
|
||||
@ -134,7 +136,7 @@ public class HtmlSerialFieldWriter extends FieldWriterImpl
|
||||
if (!description.isEmpty()) {
|
||||
Content serialFieldContent = writer.commentTagsToContent(field,
|
||||
description,
|
||||
new TagletWriterImpl.Context(false, false));
|
||||
new TagletWriter.Context(false, false));
|
||||
var div = HtmlTree.DIV(HtmlStyle.block, serialFieldContent);
|
||||
content.add(div);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -34,7 +34,7 @@ import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.Text;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
import jdk.javadoc.internal.doclets.toolkit.SerializedFormWriter;
|
||||
import jdk.javadoc.internal.doclets.toolkit.taglets.TagletManager;
|
||||
import jdk.javadoc.internal.doclets.formats.html.taglets.TagletManager;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -1,985 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.formats.html;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
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 java.util.stream.Collectors;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.ModuleElement;
|
||||
import javax.lang.model.element.PackageElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
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;
|
||||
import com.sun.source.doctree.SeeTree;
|
||||
import com.sun.source.doctree.SnippetTree;
|
||||
import com.sun.source.doctree.SpecTree;
|
||||
import com.sun.source.doctree.SystemPropertyTree;
|
||||
import com.sun.source.doctree.TextTree;
|
||||
import com.sun.source.doctree.ThrowsTree;
|
||||
import com.sun.source.util.DocTreePath;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlId;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.RawHtml;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.TagName;
|
||||
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;
|
||||
import jdk.javadoc.internal.doclets.toolkit.taglets.TagletWriter;
|
||||
import jdk.javadoc.internal.doclets.toolkit.taglets.snippet.Style;
|
||||
import jdk.javadoc.internal.doclets.toolkit.taglets.snippet.StyledText;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocLink;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
|
||||
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.
|
||||
*/
|
||||
public class TagletWriterImpl extends TagletWriter {
|
||||
/**
|
||||
* A class that provides the information about the enclosing context for
|
||||
* a series of {@code DocTree} nodes.
|
||||
* This context may be used to determine the content that should be generated from the tree nodes.
|
||||
*/
|
||||
static class Context {
|
||||
/**
|
||||
* Whether or not the trees are appearing in a context of just the first sentence,
|
||||
* such as in the summary table of the enclosing element.
|
||||
*/
|
||||
final boolean isFirstSentence;
|
||||
/**
|
||||
* Whether or not the trees are appearing in the "summary" section of the
|
||||
* page for a declaration.
|
||||
*/
|
||||
final boolean inSummary;
|
||||
/**
|
||||
* The set of enclosing kinds of tags.
|
||||
*/
|
||||
final Set<DocTree.Kind> inTags;
|
||||
|
||||
/**
|
||||
* Creates an outermost context, with no enclosing tags.
|
||||
*
|
||||
* @param isFirstSentence {@code true} if the trees are appearing in a context of just the
|
||||
* first sentence and {@code false} otherwise
|
||||
* @param inSummary {@code true} if the trees are appearing in the "summary" section
|
||||
* of the page for a declaration and {@code false} otherwise
|
||||
*/
|
||||
Context(boolean isFirstSentence, boolean inSummary) {
|
||||
this(isFirstSentence, inSummary, EnumSet.noneOf(DocTree.Kind.class));
|
||||
}
|
||||
|
||||
private Context(boolean isFirstSentence, boolean inSummary, Set<DocTree.Kind> inTags) {
|
||||
this.isFirstSentence = isFirstSentence;
|
||||
this.inSummary = inSummary;
|
||||
this.inTags = inTags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code Context} that includes an extra tag kind in the set of enclosing
|
||||
* kinds of tags.
|
||||
*
|
||||
* @param tree the enclosing tree
|
||||
*
|
||||
* @return the new {@code Context}
|
||||
*/
|
||||
Context within(DocTree tree) {
|
||||
var newInTags = EnumSet.copyOf(inTags);
|
||||
newInTags.add(tree.getKind());
|
||||
return new Context(isFirstSentence, inSummary, newInTags);
|
||||
}
|
||||
}
|
||||
|
||||
private final HtmlDocletWriter htmlWriter;
|
||||
private final HtmlConfiguration configuration;
|
||||
private final HtmlOptions options;
|
||||
private final Utils utils;
|
||||
private final Resources resources;
|
||||
|
||||
private final Messages messages;
|
||||
|
||||
private final Contents contents;
|
||||
private final Context context;
|
||||
|
||||
// Threshold for length of @see tag label for switching from inline to block layout.
|
||||
private static final int TAG_LIST_ITEM_MAX_INLINE_LENGTH = 30;
|
||||
|
||||
/**
|
||||
* Creates a taglet writer.
|
||||
*
|
||||
* @param htmlWriter the {@code HtmlDocletWriter} for the page
|
||||
* @param isFirstSentence {@code true} if this taglet writer is being used for a
|
||||
* "first sentence" summary
|
||||
*/
|
||||
public TagletWriterImpl(HtmlDocletWriter htmlWriter, boolean isFirstSentence) {
|
||||
this(htmlWriter, isFirstSentence, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a taglet writer.
|
||||
*
|
||||
* @param htmlWriter the {@code HtmlDocletWriter} for the page
|
||||
* @param isFirstSentence {@code true} if this taglet writer is being used for a
|
||||
* "first sentence" summary, and {@code false} otherwise
|
||||
* @param inSummary {@code true} if this taglet writer is being used for the content
|
||||
* of a {@code {@summary ...}} tag, and {@code false} otherwise
|
||||
*/
|
||||
public TagletWriterImpl(HtmlDocletWriter htmlWriter, boolean isFirstSentence, boolean inSummary) {
|
||||
this(htmlWriter, new Context(isFirstSentence, inSummary));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a taglet writer.
|
||||
*
|
||||
* @param htmlWriter the {@code HtmlDocletWriter} for the page
|
||||
* @param context the enclosing context for any tags
|
||||
*/
|
||||
public TagletWriterImpl(HtmlDocletWriter htmlWriter, Context context) {
|
||||
super(context.isFirstSentence);
|
||||
this.htmlWriter = htmlWriter;
|
||||
this.context = context;
|
||||
configuration = htmlWriter.configuration;
|
||||
options = configuration.getOptions();
|
||||
utils = configuration.utils;
|
||||
messages = configuration.messages;
|
||||
resources = configuration.getDocResources();
|
||||
contents = configuration.getContents();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getOutputInstance() {
|
||||
return new ContentBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Content codeTagOutput(Element element, LiteralTree tag) {
|
||||
return HtmlTree.CODE(Text.of(Text.normalizeNewlines(tag.getBody().getBody())));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Content indexTagOutput(Element element, IndexTree tag) {
|
||||
CommentHelper ch = utils.getCommentHelper(element);
|
||||
|
||||
DocTree searchTerm = tag.getSearchTerm();
|
||||
String tagText = (searchTerm instanceof TextTree tt) ? tt.getBody() : "";
|
||||
if (tagText.charAt(0) == '"' && tagText.charAt(tagText.length() - 1) == '"') {
|
||||
tagText = tagText.substring(1, tagText.length() - 1);
|
||||
}
|
||||
tagText = tagText.replaceAll("\\s+", " ");
|
||||
|
||||
Content desc = htmlWriter.commentTagsToContent(element, tag.getDescription(), context.within(tag));
|
||||
String descText = extractText(desc);
|
||||
|
||||
return createAnchorAndSearchIndex(element, tagText, descText, tag);
|
||||
}
|
||||
|
||||
// ugly but simple;
|
||||
// alternatives would be to walk the Content's tree structure, or to add new functionality to Content
|
||||
private String extractText(Content c) {
|
||||
return c.toString().replaceAll("<[^>]+>", "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getDocRootOutput() {
|
||||
String path;
|
||||
if (htmlWriter.pathToRoot.isEmpty())
|
||||
path = ".";
|
||||
else
|
||||
path = htmlWriter.pathToRoot.getPath();
|
||||
return Text.of(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content deprecatedTagOutput(Element element) {
|
||||
ContentBuilder result = new ContentBuilder();
|
||||
CommentHelper ch = utils.getCommentHelper(element);
|
||||
List<? extends DeprecatedTree> deprs = utils.getDeprecatedTrees(element);
|
||||
if (utils.isTypeElement(element)) {
|
||||
if (utils.isDeprecated(element)) {
|
||||
result.add(HtmlTree.SPAN(HtmlStyle.deprecatedLabel,
|
||||
htmlWriter.getDeprecatedPhrase(element)));
|
||||
if (!deprs.isEmpty()) {
|
||||
List<? extends DocTree> commentTrees = ch.getDescription(deprs.get(0));
|
||||
if (!commentTrees.isEmpty()) {
|
||||
result.add(commentTagsToOutput(element, null, commentTrees, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (utils.isDeprecated(element)) {
|
||||
result.add(HtmlTree.SPAN(HtmlStyle.deprecatedLabel,
|
||||
htmlWriter.getDeprecatedPhrase(element)));
|
||||
if (!deprs.isEmpty()) {
|
||||
List<? extends DocTree> bodyTrees = ch.getBody(deprs.get(0));
|
||||
Content body = commentTagsToOutput(element, null, bodyTrees, false);
|
||||
if (!body.isEmpty())
|
||||
result.add(HtmlTree.DIV(HtmlStyle.deprecationComment, body));
|
||||
}
|
||||
} else {
|
||||
Element ee = utils.getEnclosingTypeElement(element);
|
||||
if (utils.isDeprecated(ee)) {
|
||||
result.add(HtmlTree.SPAN(HtmlStyle.deprecatedLabel,
|
||||
htmlWriter.getDeprecatedPhrase(ee)));
|
||||
}
|
||||
}
|
||||
}
|
||||
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 refSignature = ch.getReferencedSignature(linkRef);
|
||||
|
||||
return linkSeeReferenceOutput(element,
|
||||
tag,
|
||||
refSignature,
|
||||
ch.getReferencedElement(tag),
|
||||
(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(Text.normalizeNewlines(tag.getBody().getBody()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getParamHeader(ParamTaglet.ParamKind kind) {
|
||||
Content header = switch (kind) {
|
||||
case PARAMETER -> contents.parameters;
|
||||
case TYPE_PARAMETER -> contents.typeParameters;
|
||||
case RECORD_COMPONENT -> contents.recordComponents;
|
||||
default -> throw new IllegalArgumentException(kind.toString());
|
||||
};
|
||||
return HtmlTree.DT(header);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content paramTagOutput(Element element, ParamTree paramTag, String paramName) {
|
||||
ContentBuilder body = new ContentBuilder();
|
||||
CommentHelper ch = utils.getCommentHelper(element);
|
||||
// define id attributes for state components so that generated descriptions may refer to them
|
||||
boolean defineID = (element.getKind() == ElementKind.RECORD)
|
||||
&& !paramTag.isTypeParameter();
|
||||
Content nameContent = Text.of(paramName);
|
||||
body.add(HtmlTree.CODE(defineID ? HtmlTree.SPAN_ID(HtmlIds.forParam(paramName), nameContent) : nameContent));
|
||||
body.add(" - ");
|
||||
List<? extends DocTree> description = ch.getDescription(paramTag);
|
||||
body.add(htmlWriter.commentTagsToContent(element, description, context.within(paramTag)));
|
||||
return HtmlTree.DD(body);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content returnTagOutput(Element element, ReturnTree returnTag, boolean inline) {
|
||||
CommentHelper ch = utils.getCommentHelper(element);
|
||||
List<? extends DocTree> desc = ch.getDescription(returnTag);
|
||||
Content content = htmlWriter.commentTagsToContent(element, desc, context.within(returnTag));
|
||||
return inline
|
||||
? new ContentBuilder(contents.getContent("doclet.Returns_0", content))
|
||||
: new ContentBuilder(HtmlTree.DT(contents.returns), HtmlTree.DD(content));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content seeTagOutput(Element holder, List<? extends SeeTree> seeTags) {
|
||||
List<Content> links = new ArrayList<>();
|
||||
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) {
|
||||
//Automatically add link to constant values page for constant fields.
|
||||
DocPath constantsPath =
|
||||
htmlWriter.pathToRoot.resolve(DocPaths.CONSTANT_VALUES);
|
||||
String whichConstant =
|
||||
writer.getTypeElement().getQualifiedName() + "." +
|
||||
utils.getSimpleName(holder);
|
||||
DocLink link = constantsPath.fragment(whichConstant);
|
||||
links.add(htmlWriter.links.createLink(link,
|
||||
contents.getContent("doclet.Constants_Summary")));
|
||||
}
|
||||
if (utils.isClass(holder) && utils.isSerializable((TypeElement)holder)) {
|
||||
//Automatically add link to serialized form page for serializable classes.
|
||||
if (SerializedFormBuilder.serialInclude(utils, holder) &&
|
||||
SerializedFormBuilder.serialInclude(utils, utils.containingPackage(holder))) {
|
||||
DocPath serialPath = htmlWriter.pathToRoot.resolve(DocPaths.SERIALIZED_FORM);
|
||||
DocLink link = serialPath.fragment(utils.getFullyQualifiedName(holder));
|
||||
links.add(htmlWriter.links.createLink(link,
|
||||
contents.getContent("doclet.Serialized_Form")));
|
||||
}
|
||||
}
|
||||
if (links.isEmpty()) {
|
||||
return Text.EMPTY;
|
||||
}
|
||||
// Use a different style if any link label is longer than 30 chars or contains commas.
|
||||
boolean hasLongLabels = links.stream().anyMatch(this::isLongOrHasComma);
|
||||
var seeList = HtmlTree.UL(hasLongLabels ? HtmlStyle.tagListLong : HtmlStyle.tagList);
|
||||
links.stream()
|
||||
.filter(Predicate.not(Content::isEmpty))
|
||||
.forEach(item -> seeList.add(HtmlTree.LI(item)));
|
||||
|
||||
return new ContentBuilder(
|
||||
HtmlTree.DT(contents.seeAlso),
|
||||
HtmlTree.DD(seeList));
|
||||
}
|
||||
|
||||
private boolean isLongOrHasComma(Content c) {
|
||||
String s = c.toString()
|
||||
.replaceAll("<.*?>", "") // ignore HTML
|
||||
.replaceAll("&#?[A-Za-z0-9]+;", " ") // entities count as a single character
|
||||
.replaceAll("\\R", "\n"); // normalize newlines
|
||||
return s.length() > TAG_LIST_ITEM_MAX_INLINE_LENGTH || s.contains(",");
|
||||
}
|
||||
|
||||
String textOf(List<? extends DocTree> trees) {
|
||||
return trees.stream()
|
||||
.filter(dt -> dt instanceof TextTree)
|
||||
.map(dt -> ((TextTree) dt).getBody().trim())
|
||||
.collect(Collectors.joining(" "));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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 refSignature = ch.getReferencedSignature(ref0);
|
||||
List<? extends DocTree> label = ref.subList(1, ref.size());
|
||||
|
||||
return linkSeeReferenceOutput(element,
|
||||
seeTag,
|
||||
refSignature,
|
||||
ch.getReferencedElement(seeTag),
|
||||
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 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,
|
||||
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 refFragment = ch.getReferencedFragment(refSignature);
|
||||
|
||||
if (refFragment == null && refMem != null) {
|
||||
refFragment = refMem.toString();
|
||||
} else if (refFragment != null && refFragment.startsWith("#")) {
|
||||
if (labelContent.isEmpty()) {
|
||||
// A non-empty label is required for fragment links as the
|
||||
// reference target does not provide a useful default label.
|
||||
htmlWriter.messages.error(ch.getDocTreePath(refTree), "doclet.link.see.no_label");
|
||||
return invalidTagOutput(resources.getText("doclet.link.see.no_label"),
|
||||
Optional.of(refSignature));
|
||||
}
|
||||
refFragment = refFragment.substring(1);
|
||||
}
|
||||
if (refClass == null) {
|
||||
ModuleElement refModule = ch.getReferencedModule(ref);
|
||||
if (refModule != null && utils.isIncluded(refModule)) {
|
||||
return htmlWriter.getModuleLink(refModule, labelContent.isEmpty() ? text : labelContent, refFragment);
|
||||
}
|
||||
//@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, refFragment);
|
||||
} 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.link.see.reference_not_found",
|
||||
new Object[] { refSignature});
|
||||
}
|
||||
return htmlWriter.invalidTagOutput(resources.getText("doclet.link.see.reference_invalid"),
|
||||
Optional.of(labelContent.isEmpty() ? text: labelContent));
|
||||
}
|
||||
}
|
||||
} else if (refFragment == null) {
|
||||
// Must be a class reference since refClass is not null and refFragment 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.LINK_TYPE_PARAMS_AND_BOUNDS, referencedType)));
|
||||
}
|
||||
labelContent = plainOrCode(isLinkPlain, Text.of(utils.getSimpleName(refClass)));
|
||||
}
|
||||
return htmlWriter.getLink(new HtmlLinkInfo(configuration, HtmlLinkInfo.Kind.PLAIN, refClass)
|
||||
.label(labelContent));
|
||||
} else if (refMem == null) {
|
||||
// This is a fragment reference since refClass and refFragment are not null but refMem is null.
|
||||
return htmlWriter.getLink(new HtmlLinkInfo(configuration, HtmlLinkInfo.Kind.PLAIN, refClass)
|
||||
.label(labelContent)
|
||||
.fragment(refFragment)
|
||||
.style(null));
|
||||
} 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.link.see.reference_not_accessible",
|
||||
new Object[] { utils.getFullyQualifiedName(containing)});
|
||||
} else {
|
||||
if (!configuration.isDocLintReferenceGroupEnabled()) {
|
||||
reportWarning.accept("doclet.link.see.reference_not_found",
|
||||
new Object[] { refSignature });
|
||||
}
|
||||
}
|
||||
}
|
||||
String refMemName = refFragment;
|
||||
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.SHOW_PREVIEW, 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);
|
||||
ContentBuilder body = new ContentBuilder();
|
||||
boolean many = false;
|
||||
for (DocTree simpleTag : simpleTags) {
|
||||
if (many) {
|
||||
body.add(", ");
|
||||
}
|
||||
List<? extends DocTree> bodyTags = ch.getBody(simpleTag);
|
||||
body.add(htmlWriter.commentTagsToContent(element, bodyTags, context.within(simpleTag)));
|
||||
many = true;
|
||||
}
|
||||
return new ContentBuilder(
|
||||
HtmlTree.DT(RawHtml.of(header)),
|
||||
HtmlTree.DD(body));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Content snippetTagOutput(Element element, SnippetTree tag, StyledText content,
|
||||
String id, String lang) {
|
||||
var pre = new HtmlTree(TagName.PRE).setStyle(HtmlStyle.snippet);
|
||||
if (id != null && !id.isBlank()) {
|
||||
pre.put(HtmlAttr.ID, id);
|
||||
}
|
||||
var code = new HtmlTree(TagName.CODE)
|
||||
.addUnchecked(Text.EMPTY); // Make sure the element is always rendered
|
||||
if (lang != null && !lang.isBlank()) {
|
||||
code.addStyle("language-" + lang);
|
||||
}
|
||||
|
||||
content.consumeBy((styles, sequence) -> {
|
||||
CharSequence text = Text.normalizeNewlines(sequence);
|
||||
if (styles.isEmpty()) {
|
||||
code.add(text);
|
||||
} else {
|
||||
Element e = null;
|
||||
String t = null;
|
||||
boolean linkEncountered = false;
|
||||
boolean markupEncountered = false;
|
||||
Set<String> classes = new HashSet<>();
|
||||
for (Style s : styles) {
|
||||
if (s instanceof Style.Name n) {
|
||||
classes.add(n.name());
|
||||
} else if (s instanceof Style.Link l) {
|
||||
assert !linkEncountered; // TODO: do not assert; pick the first link report on subsequent
|
||||
linkEncountered = true;
|
||||
t = l.target();
|
||||
e = getLinkedElement(element, t);
|
||||
if (e == null) {
|
||||
// TODO: diagnostic output
|
||||
}
|
||||
} else if (s instanceof Style.Markup) {
|
||||
markupEncountered = true;
|
||||
break;
|
||||
} else {
|
||||
// TODO: transform this if...else into an exhaustive
|
||||
// switch over the sealed Style hierarchy when "Pattern
|
||||
// Matching for switch" has been implemented (JEP 406
|
||||
// and friends)
|
||||
throw new AssertionError(styles);
|
||||
}
|
||||
}
|
||||
Content c;
|
||||
if (markupEncountered) {
|
||||
return;
|
||||
} else if (linkEncountered) {
|
||||
assert e != null;
|
||||
//disable preview tagging inside the snippets:
|
||||
PreviewFlagProvider prevPreviewProvider = utils.setPreviewFlagProvider(el -> false);
|
||||
try {
|
||||
c = linkSeeReferenceOutput(element,
|
||||
null,
|
||||
t,
|
||||
e,
|
||||
false, // TODO: for now
|
||||
Text.of(sequence.toString()),
|
||||
(key, args) -> { /* TODO: report diagnostic */ });
|
||||
} finally {
|
||||
utils.setPreviewFlagProvider(prevPreviewProvider);
|
||||
}
|
||||
} else {
|
||||
c = HtmlTree.SPAN(Text.of(text));
|
||||
classes.forEach(((HtmlTree) c)::addStyle);
|
||||
}
|
||||
code.add(c);
|
||||
}
|
||||
});
|
||||
String copyText = resources.getText("doclet.Copy_to_clipboard");
|
||||
String copiedText = resources.getText("doclet.Copied_to_clipboard");
|
||||
String copySnippetText = resources.getText("doclet.Copy_snippet_to_clipboard");
|
||||
var snippetContainer = HtmlTree.DIV(HtmlStyle.snippetContainer,
|
||||
new HtmlTree(TagName.BUTTON)
|
||||
.add(HtmlTree.SPAN(Text.of(copyText))
|
||||
.put(HtmlAttr.DATA_COPIED, copiedText))
|
||||
.add(new HtmlTree(TagName.IMG)
|
||||
.put(HtmlAttr.SRC, htmlWriter.pathToRoot.resolve(DocPaths.CLIPBOARD_SVG).getPath())
|
||||
.put(HtmlAttr.ALT, copySnippetText))
|
||||
.addStyle(HtmlStyle.copy)
|
||||
.addStyle(HtmlStyle.snippetCopy)
|
||||
.put(HtmlAttr.ARIA_LABEL, copySnippetText)
|
||||
.put(HtmlAttr.ONCLICK, "copySnippet(this)"));
|
||||
return snippetContainer.add(pre.add(code));
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the element that is linked from the context of the referrer using
|
||||
* the provided signature; returns null if such element could not be found.
|
||||
*
|
||||
* This method is to be used when it is the target of the link that is
|
||||
* important, not the container of the link (e.g. was it an @see,
|
||||
* @link/@linkplain or @snippet tags, etc.)
|
||||
*/
|
||||
public Element getLinkedElement(Element referer, String signature) {
|
||||
var factory = utils.docTrees.getDocTreeFactory();
|
||||
var docCommentTree = utils.getDocCommentTree(referer);
|
||||
var rootPath = new DocTreePath(utils.getTreePath(referer), docCommentTree);
|
||||
var reference = factory.newReferenceTree(signature);
|
||||
var fabricatedPath = new DocTreePath(rootPath, reference);
|
||||
return utils.docTrees.getElement(fabricatedPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content specTagOutput(Element holder, List<? extends SpecTree> specTags) {
|
||||
if (specTags.isEmpty()) {
|
||||
return Text.EMPTY;
|
||||
}
|
||||
|
||||
List<Content> links = specTags.stream()
|
||||
.map(st -> specTagToContent(holder, st))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Use a different style if any link label is longer than 30 chars or contains commas.
|
||||
boolean hasLongLabels = links.stream().anyMatch(this::isLongOrHasComma);
|
||||
var specList = HtmlTree.UL(hasLongLabels ? HtmlStyle.tagListLong : HtmlStyle.tagList);
|
||||
links.stream()
|
||||
.filter(Predicate.not(Content::isEmpty))
|
||||
.forEach(item -> specList.add(HtmlTree.LI(item)));
|
||||
|
||||
return new ContentBuilder(
|
||||
HtmlTree.DT(contents.externalSpecifications),
|
||||
HtmlTree.DD(specList));
|
||||
}
|
||||
|
||||
private Content specTagToContent(Element holder, SpecTree specTree) {
|
||||
String specTreeURL = specTree.getURL().getBody();
|
||||
List<? extends DocTree> specTreeLabel = specTree.getTitle();
|
||||
Content label = htmlWriter.commentTagsToContent(holder, specTreeLabel, isFirstSentence);
|
||||
return getExternalSpecContent(holder, specTree, specTreeURL,
|
||||
textOf(specTreeLabel).replaceAll("\\s+", " "), label);
|
||||
}
|
||||
|
||||
Content getExternalSpecContent(Element holder, DocTree docTree, String url, String searchText, Content title) {
|
||||
URI specURI;
|
||||
try {
|
||||
// Use the canonical title of the spec if one is available
|
||||
specURI = new URI(url);
|
||||
} catch (URISyntaxException e) {
|
||||
CommentHelper ch = utils.getCommentHelper(holder);
|
||||
DocTreePath dtp = ch.getDocTreePath(docTree);
|
||||
htmlWriter.messages.error(dtp, "doclet.Invalid_URL", e.getMessage());
|
||||
specURI = null;
|
||||
}
|
||||
|
||||
Content titleWithAnchor = createAnchorAndSearchIndex(holder,
|
||||
searchText,
|
||||
title,
|
||||
resources.getText("doclet.External_Specification"),
|
||||
docTree);
|
||||
|
||||
if (specURI == null) {
|
||||
return titleWithAnchor;
|
||||
} else {
|
||||
return HtmlTree.A(htmlWriter.resolveExternalSpecURI(specURI), titleWithAnchor);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Content systemPropertyTagOutput(Element element, SystemPropertyTree tag) {
|
||||
String tagText = tag.getPropertyName().toString();
|
||||
return HtmlTree.CODE(createAnchorAndSearchIndex(element, tagText,
|
||||
resources.getText("doclet.System_Property"), tag));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getThrowsHeader() {
|
||||
return HtmlTree.DT(contents.throws_);
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
private Content throwsTagOutput(Element element, ThrowsTree throwsTag, TypeMirror substituteType) {
|
||||
ContentBuilder body = new ContentBuilder();
|
||||
CommentHelper ch = utils.getCommentHelper(element);
|
||||
Element exception = ch.getException(throwsTag);
|
||||
Content excName;
|
||||
if (substituteType != null) {
|
||||
excName = htmlWriter.getLink(new HtmlLinkInfo(configuration, HtmlLinkInfo.Kind.PLAIN,
|
||||
substituteType));
|
||||
} else if (exception == null) {
|
||||
excName = Text.of(throwsTag.getExceptionName().toString());
|
||||
} else if (exception.asType() == null) {
|
||||
excName = Text.of(utils.getFullyQualifiedName(exception));
|
||||
} else {
|
||||
HtmlLinkInfo link = new HtmlLinkInfo(configuration, HtmlLinkInfo.Kind.PLAIN,
|
||||
exception.asType());
|
||||
excName = htmlWriter.getLink(link);
|
||||
}
|
||||
body.add(HtmlTree.CODE(excName));
|
||||
List<? extends DocTree> description = ch.getDescription(throwsTag);
|
||||
Content desc = htmlWriter.commentTagsToContent(element, description, context.within(throwsTag));
|
||||
if (desc != null && !desc.isEmpty()) {
|
||||
body.add(" - ");
|
||||
body.add(desc);
|
||||
}
|
||||
return HtmlTree.DD(body);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content throwsTagOutput(TypeMirror throwsType, Optional<Content> content) {
|
||||
var linkInfo = new HtmlLinkInfo(configuration, HtmlLinkInfo.Kind.PLAIN, throwsType);
|
||||
var link = htmlWriter.getLink(linkInfo);
|
||||
var concat = new ContentBuilder(HtmlTree.CODE(link));
|
||||
if (content.isPresent()) {
|
||||
concat.add(" - ");
|
||||
concat.add(content.get());
|
||||
}
|
||||
return HtmlTree.DD(concat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content valueTagOutput(VariableElement field, String constantVal, boolean includeLink) {
|
||||
return includeLink
|
||||
? htmlWriter.getDocLink(HtmlLinkInfo.Kind.LINK_TYPE_PARAMS_AND_BOUNDS, field, constantVal)
|
||||
: Text.of(constantVal);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Content invalidTagOutput(String summary, Optional<String> detail) {
|
||||
return htmlWriter.invalidTagOutput(summary,
|
||||
detail.isEmpty() || detail.get().isEmpty()
|
||||
? Optional.empty()
|
||||
: Optional.of(Text.of(Text.normalizeNewlines(detail.get()))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content commentTagsToOutput(DocTree holder, List<? extends DocTree> tags) {
|
||||
return commentTagsToOutput(null, holder, tags, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content commentTagsToOutput(Element element, List<? extends DocTree> tags) {
|
||||
return commentTagsToOutput(element, null, tags, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content commentTagsToOutput(Element holder,
|
||||
DocTree holderTag,
|
||||
List<? extends DocTree> tags,
|
||||
boolean isFirstSentence)
|
||||
{
|
||||
return htmlWriter.commentTagsToContent(holder,
|
||||
tags, holderTag == null ? context : context.within(holderTag));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseConfiguration configuration() {
|
||||
return configuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TypeElement getCurrentPageElement() {
|
||||
return htmlWriter.getCurrentPageElement();
|
||||
}
|
||||
|
||||
public HtmlDocletWriter getHtmlWriter() {
|
||||
return htmlWriter;
|
||||
}
|
||||
|
||||
private Content createAnchorAndSearchIndex(Element element, String tagText, String desc, DocTree tree) {
|
||||
return createAnchorAndSearchIndex(element, tagText, Text.of(tagText), desc, tree);
|
||||
}
|
||||
|
||||
@SuppressWarnings("preview")
|
||||
private Content createAnchorAndSearchIndex(Element element, String tagText, Content tagContent, String desc, DocTree tree) {
|
||||
Content result = null;
|
||||
if (context.isFirstSentence && context.inSummary || context.inTags.contains(DocTree.Kind.INDEX)) {
|
||||
result = tagContent;
|
||||
} else {
|
||||
HtmlId id = HtmlIds.forText(tagText, htmlWriter.indexAnchorTable);
|
||||
result = HtmlTree.SPAN(id, HtmlStyle.searchTagResult, tagContent);
|
||||
if (options.createIndex() && !tagText.isEmpty()) {
|
||||
String holder = getHolderName(element);
|
||||
IndexItem item = IndexItem.of(element, tree, tagText, holder, desc,
|
||||
new DocLink(htmlWriter.path, id.name()));
|
||||
configuration.mainIndex.add(item);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
String getHolderName(Element element) {
|
||||
return new SimpleElementVisitor14<String, Void>() {
|
||||
|
||||
@Override
|
||||
public String visitModule(ModuleElement e, Void p) {
|
||||
return resources.getText("doclet.module")
|
||||
+ " " + utils.getFullyQualifiedName(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitPackage(PackageElement e, Void p) {
|
||||
return resources.getText("doclet.package")
|
||||
+ " " + utils.getFullyQualifiedName(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitType(TypeElement e, Void p) {
|
||||
return utils.getTypeElementKindName(e, true)
|
||||
+ " " + utils.getFullyQualifiedName(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitExecutable(ExecutableElement e, Void p) {
|
||||
return utils.getFullyQualifiedName(utils.getEnclosingTypeElement(e))
|
||||
+ "." + utils.getSimpleName(e)
|
||||
+ utils.flatSignature(e, htmlWriter.getCurrentPageElement());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitVariable(VariableElement e, Void p) {
|
||||
return utils.getFullyQualifiedName(utils.getEnclosingTypeElement(e))
|
||||
+ "." + utils.getSimpleName(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitUnknown(Element e, Void p) {
|
||||
if (e instanceof DocletElement de) {
|
||||
return switch (de.getSubKind()) {
|
||||
case OVERVIEW -> resources.getText("doclet.Overview");
|
||||
case DOCFILE -> getHolderName(de);
|
||||
};
|
||||
} else {
|
||||
return super.visitUnknown(e, p);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String defaultAction(Element e, Void p) {
|
||||
return utils.getFullyQualifiedName(e);
|
||||
}
|
||||
}.visit(element);
|
||||
}
|
||||
|
||||
private String getHolderName(DocletElement de) {
|
||||
PackageElement pe = de.getPackageElement();
|
||||
if (pe.isUnnamed()) {
|
||||
// if package is unnamed use enclosing module only if it is named
|
||||
Element ee = pe.getEnclosingElement();
|
||||
if (ee instanceof ModuleElement && !((ModuleElement)ee).isUnnamed()) {
|
||||
return resources.getText("doclet.module") + " " + utils.getFullyQualifiedName(ee);
|
||||
}
|
||||
return pe.toString(); // "Unnamed package" or similar
|
||||
}
|
||||
return resources.getText("doclet.package") + " " + utils.getFullyQualifiedName(pe);
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,35 +23,56 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets;
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import com.sun.source.doctree.UnknownBlockTagTree;
|
||||
|
||||
import jdk.javadoc.doclet.Taglet.Location;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Messages;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Resources;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.Utils;
|
||||
|
||||
/**
|
||||
* A base class that implements the {@link Taglet} interface.
|
||||
*/
|
||||
public class BaseTaglet implements Taglet {
|
||||
// The following members are global to the lifetime of the doclet
|
||||
protected final HtmlConfiguration config;
|
||||
protected final Messages messages;
|
||||
protected final Resources resources;
|
||||
protected final Utils utils;
|
||||
|
||||
// The following members are specific to the instance of the taglet
|
||||
protected final DocTree.Kind tagKind;
|
||||
protected final String name;
|
||||
private final boolean inline;
|
||||
private final Set<Location> sites;
|
||||
|
||||
BaseTaglet(DocTree.Kind tagKind, boolean inline, Set<Location> sites) {
|
||||
this(tagKind.tagName, tagKind, inline, sites);
|
||||
// The following is dynamically set for the duration of the methods
|
||||
// getInlineTagOutput and getAllBlockTagOutput
|
||||
// by those taglets that need to refer to it
|
||||
protected TagletWriter tagletWriter;
|
||||
|
||||
public BaseTaglet(HtmlConfiguration config, DocTree.Kind tagKind, boolean inline, Set<Location> sites) {
|
||||
this(config, tagKind.tagName, tagKind, inline, sites);
|
||||
}
|
||||
|
||||
BaseTaglet(String name, boolean inline, Set<Location> sites) {
|
||||
this(name, inline ? DocTree.Kind.UNKNOWN_INLINE_TAG : DocTree.Kind.UNKNOWN_BLOCK_TAG, inline, sites);
|
||||
protected BaseTaglet(HtmlConfiguration config, String name, boolean inline, Set<Location> sites) {
|
||||
this(config, name, inline ? DocTree.Kind.UNKNOWN_INLINE_TAG : DocTree.Kind.UNKNOWN_BLOCK_TAG, inline, sites);
|
||||
}
|
||||
|
||||
private BaseTaglet(String name, DocTree.Kind tagKind, boolean inline, Set<Location> sites) {
|
||||
private BaseTaglet(HtmlConfiguration config, String name, DocTree.Kind tagKind, boolean inline, Set<Location> sites) {
|
||||
this.config = config;
|
||||
this.messages = config.getMessages();
|
||||
this.resources = config.getDocResources();
|
||||
this.utils = config.utils;
|
||||
|
||||
this.name = name;
|
||||
this.tagKind = tagKind;
|
||||
this.inline = inline;
|
||||
@ -63,41 +84,6 @@ public class BaseTaglet implements Taglet {
|
||||
return sites;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean inField() {
|
||||
return sites.contains(Location.FIELD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean inConstructor() {
|
||||
return sites.contains(Location.CONSTRUCTOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean inMethod() {
|
||||
return sites.contains(Location.METHOD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean inOverview() {
|
||||
return sites.contains(Location.OVERVIEW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean inModule() {
|
||||
return sites.contains(Location.MODULE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean inPackage() {
|
||||
return sites.contains(Location.PACKAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean inType() {
|
||||
return sites.contains(Location.TYPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isInlineTag() {
|
||||
return inline;
|
||||
@ -117,28 +103,13 @@ public class BaseTaglet implements Taglet {
|
||||
return tagKind;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this taglet accepts a {@code DocTree} node.
|
||||
* The taglet accepts a tree node if it has the same kind, and
|
||||
* if the kind is {@code UNKNOWN_BLOCK_TAG} the same tag name.
|
||||
*
|
||||
* @param tree the tree node
|
||||
* @return {@code true} if this taglet accepts this tree node
|
||||
*/
|
||||
public boolean accepts(DocTree tree) {
|
||||
return (tree.getKind() == DocTree.Kind.UNKNOWN_BLOCK_TAG
|
||||
&& tagKind == DocTree.Kind.UNKNOWN_BLOCK_TAG)
|
||||
? ((UnknownBlockTagTree) tree).getTagName().equals(name)
|
||||
: tree.getKind() == tagKind;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @implSpec This implementation throws {@link UnsupportedTagletOperationException}.
|
||||
*/
|
||||
@Override
|
||||
public Content getInlineTagOutput(Element element, DocTree tag, TagletWriter writer) {
|
||||
public Content getInlineTagOutput(Element element, DocTree tag, TagletWriter tagletWriter) {
|
||||
throw new UnsupportedTagletOperationException("Method not supported in taglet " + getName() + ".");
|
||||
}
|
||||
|
||||
@ -148,7 +119,7 @@ public class BaseTaglet implements Taglet {
|
||||
* @implSpec This implementation throws {@link UnsupportedTagletOperationException}
|
||||
*/
|
||||
@Override
|
||||
public Content getAllBlockTagOutput(Element holder, TagletWriter writer) {
|
||||
public Content getAllBlockTagOutput(Element holder, TagletWriter tagletWriter) {
|
||||
throw new UnsupportedTagletOperationException("Method not supported in taglet " + getName() + ".");
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
|
||||
import com.sun.source.doctree.DeprecatedTree;
|
||||
import com.sun.source.doctree.DocTree;
|
||||
|
||||
import jdk.javadoc.doclet.Taglet;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
|
||||
|
||||
/**
|
||||
* A taglet that represents the {@code @deprecated} tag.
|
||||
*/
|
||||
public class DeprecatedTaglet extends BaseTaglet {
|
||||
|
||||
DeprecatedTaglet(HtmlConfiguration config) {
|
||||
super(config, DocTree.Kind.DEPRECATED, false,
|
||||
EnumSet.of(Taglet.Location.MODULE, Taglet.Location.TYPE, Taglet.Location.CONSTRUCTOR, Taglet.Location.METHOD, Taglet.Location.FIELD));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getAllBlockTagOutput(Element element, TagletWriter tagletWriter) {
|
||||
var htmlWriter = tagletWriter.htmlWriter;
|
||||
|
||||
ContentBuilder result = new ContentBuilder();
|
||||
CommentHelper ch = utils.getCommentHelper(element);
|
||||
List<? extends DeprecatedTree> deprs = utils.getDeprecatedTrees(element);
|
||||
if (utils.isTypeElement(element)) {
|
||||
if (utils.isDeprecated(element)) {
|
||||
result.add(HtmlTree.SPAN(HtmlStyle.deprecatedLabel,
|
||||
htmlWriter.getDeprecatedPhrase(element)));
|
||||
if (!deprs.isEmpty()) {
|
||||
List<? extends DocTree> commentTrees = ch.getDescription(deprs.get(0));
|
||||
if (!commentTrees.isEmpty()) {
|
||||
result.add(tagletWriter.commentTagsToOutput(element, null, commentTrees, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (utils.isDeprecated(element)) {
|
||||
result.add(HtmlTree.SPAN(HtmlStyle.deprecatedLabel,
|
||||
htmlWriter.getDeprecatedPhrase(element)));
|
||||
if (!deprs.isEmpty()) {
|
||||
List<? extends DocTree> bodyTrees = ch.getBody(deprs.get(0));
|
||||
Content body = tagletWriter.commentTagsToOutput(element, null, bodyTrees, false);
|
||||
if (!body.isEmpty())
|
||||
result.add(HtmlTree.DIV(HtmlStyle.deprecationComment, body));
|
||||
}
|
||||
} else {
|
||||
Element ee = utils.getEnclosingTypeElement(element);
|
||||
if (utils.isDeprecated(ee)) {
|
||||
result.add(HtmlTree.SPAN(HtmlStyle.deprecatedLabel,
|
||||
htmlWriter.getDeprecatedPhrase(ee)));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,13 +23,17 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets;
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import jdk.javadoc.doclet.Taglet.Location;
|
||||
|
||||
import jdk.javadoc.doclet.Taglet;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.Text;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
|
||||
/**
|
||||
@ -38,16 +42,14 @@ import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
* directory.
|
||||
*/
|
||||
public class DocRootTaglet extends BaseTaglet {
|
||||
|
||||
/**
|
||||
* Construct a new DocRootTaglet.
|
||||
*/
|
||||
public DocRootTaglet() {
|
||||
super(DocTree.Kind.DOC_ROOT, true, EnumSet.allOf(Location.class));
|
||||
DocRootTaglet(HtmlConfiguration config) {
|
||||
super(config, DocTree.Kind.DOC_ROOT, true, EnumSet.allOf(Taglet.Location.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getInlineTagOutput(Element holder, DocTree tag, TagletWriter writer) {
|
||||
return writer.getDocRootOutput();
|
||||
public Content getInlineTagOutput(Element holder, DocTree tag, TagletWriter tagletWriter) {
|
||||
var htmlWriter = tagletWriter.htmlWriter;
|
||||
var pathToRoot = htmlWriter.pathToRoot;
|
||||
return Text.of(pathToRoot.isEmpty() ? "." : pathToRoot.getPath());
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import com.sun.source.doctree.IndexTree;
|
||||
import com.sun.source.doctree.TextTree;
|
||||
|
||||
import jdk.javadoc.doclet.Taglet;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
|
||||
/**
|
||||
* An inline taglet used to index a word or a phrase.
|
||||
* The enclosed text is interpreted as not containing HTML markup or
|
||||
* nested javadoc tags.
|
||||
*/
|
||||
public class IndexTaglet extends BaseTaglet {
|
||||
|
||||
IndexTaglet(HtmlConfiguration config) {
|
||||
super(config, DocTree.Kind.INDEX, true, EnumSet.allOf(Taglet.Location.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getInlineTagOutput(Element element, DocTree tag, TagletWriter tagletWriter) {
|
||||
var context = tagletWriter.context;
|
||||
var indexTree = (IndexTree) tag;
|
||||
|
||||
DocTree searchTerm = indexTree.getSearchTerm();
|
||||
String tagText = (searchTerm instanceof TextTree tt) ? tt.getBody() : "";
|
||||
if (tagText.charAt(0) == '"' && tagText.charAt(tagText.length() - 1) == '"') {
|
||||
tagText = tagText.substring(1, tagText.length() - 1);
|
||||
}
|
||||
tagText = tagText.replaceAll("\\s+", " ");
|
||||
|
||||
Content desc = tagletWriter.htmlWriter.commentTagsToContent(element, indexTree.getDescription(), context.within(indexTree));
|
||||
String descText = extractText(desc);
|
||||
|
||||
return tagletWriter.createAnchorAndSearchIndex(element, tagText, descText, tag);
|
||||
}
|
||||
|
||||
// ugly but simple;
|
||||
// alternatives would be to walk the Content's tree structure, or to add new functionality to Content
|
||||
private String extractText(Content c) {
|
||||
return c.toString().replaceAll("<[^>]+>", "");
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets;
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
@ -36,10 +36,10 @@ import javax.lang.model.element.TypeElement;
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import com.sun.source.doctree.InheritDocTree;
|
||||
import com.sun.source.util.DocTreePath;
|
||||
|
||||
import jdk.javadoc.doclet.Taglet.Location;
|
||||
import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Messages;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocFinder;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocFinder.Result;
|
||||
@ -54,8 +54,8 @@ public class InheritDocTaglet extends BaseTaglet {
|
||||
/**
|
||||
* Construct a new InheritDocTaglet.
|
||||
*/
|
||||
public InheritDocTaglet() {
|
||||
super(DocTree.Kind.INHERIT_DOC, true, EnumSet.of(Location.METHOD));
|
||||
InheritDocTaglet(HtmlConfiguration config) {
|
||||
super(config, DocTree.Kind.INHERIT_DOC, true, EnumSet.of(Location.METHOD));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -76,9 +76,6 @@ public class InheritDocTaglet extends BaseTaglet {
|
||||
InheritDocTree inheritDoc,
|
||||
boolean isFirstSentence) {
|
||||
Content replacement = writer.getOutputInstance();
|
||||
BaseConfiguration configuration = writer.configuration();
|
||||
Messages messages = configuration.getMessages();
|
||||
Utils utils = configuration.utils;
|
||||
CommentHelper ch = utils.getCommentHelper(method);
|
||||
DocTreePath inheritDocPath = ch.getDocTreePath(inheritDoc);
|
||||
var path = inheritDocPath.getParentPath();
|
||||
@ -103,7 +100,7 @@ public class InheritDocTaglet extends BaseTaglet {
|
||||
//
|
||||
// This way we do more work in erroneous case, but less in the typical
|
||||
// case. We don't optimize for the former.
|
||||
VisibleMemberTable visibleMemberTable = configuration.getVisibleMemberTable(supertype);
|
||||
VisibleMemberTable visibleMemberTable = config.getVisibleMemberTable(supertype);
|
||||
List<Element> methods = visibleMemberTable.getAllVisibleMembers(VisibleMemberTable.Kind.METHODS);
|
||||
for (Element e : methods) {
|
||||
ExecutableElement m = (ExecutableElement) e;
|
||||
@ -142,7 +139,7 @@ public class InheritDocTaglet extends BaseTaglet {
|
||||
return replacement;
|
||||
}
|
||||
|
||||
Taglet taglet = configuration.tagletManager.getTaglet(ch.getTagName(holderTag));
|
||||
Taglet taglet = config.tagletManager.getTaglet(ch.getTagName(holderTag));
|
||||
// taglet is null if holderTag is unknown, which it shouldn't be since we reached here
|
||||
assert taglet != null;
|
||||
if (!(taglet instanceof InheritableTaglet inheritableTaglet)) {
|
||||
@ -151,7 +148,7 @@ public class InheritDocTaglet extends BaseTaglet {
|
||||
return replacement;
|
||||
}
|
||||
|
||||
InheritableTaglet.Output inheritedDoc = inheritableTaglet.inherit(method, src, holderTag, isFirstSentence, configuration);
|
||||
InheritableTaglet.Output inheritedDoc = inheritableTaglet.inherit(method, src, holderTag, isFirstSentence);
|
||||
|
||||
if (inheritedDoc.isValidInheritDocTag()) {
|
||||
if (!inheritedDoc.inlineTags().isEmpty()) {
|
||||
@ -182,6 +179,9 @@ public class InheritDocTaglet extends BaseTaglet {
|
||||
if (e.getKind() != ElementKind.METHOD) {
|
||||
return tagletWriter.getOutputInstance();
|
||||
}
|
||||
return retrieveInheritedDocumentation(tagletWriter, (ExecutableElement) e, (InheritDocTree) inheritDoc, tagletWriter.isFirstSentence);
|
||||
return retrieveInheritedDocumentation(tagletWriter,
|
||||
(ExecutableElement) e,
|
||||
(InheritDocTree) inheritDoc,
|
||||
tagletWriter.context.isFirstSentence);
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets;
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
@ -31,13 +31,12 @@ import java.util.List;
|
||||
import javax.lang.model.element.Element;
|
||||
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
|
||||
|
||||
/**
|
||||
* A taglet should implement this interface if it supports an {@code {@inheritDoc}}
|
||||
* tag or is automatically inherited if it is missing.
|
||||
*/
|
||||
public interface InheritableTaglet extends Taglet {
|
||||
public interface InheritableTaglet {
|
||||
|
||||
/*
|
||||
* Called by InheritDocTaglet on an inheritable taglet to expand {@inheritDoc S}
|
||||
@ -52,7 +51,7 @@ public interface InheritableTaglet extends Taglet {
|
||||
* In the future, this could be reworked using some other mechanism,
|
||||
* such as throwing an exception.
|
||||
*/
|
||||
Output inherit(Element dst, Element src, DocTree tag, boolean isFirstSentence, BaseConfiguration configuration);
|
||||
Output inherit(Element dst, Element src, DocTree tag, boolean isFirstSentence);
|
||||
|
||||
record Output(DocTree holderTag,
|
||||
Element holder,
|
@ -0,0 +1,278 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.ModuleElement;
|
||||
import javax.lang.model.element.PackageElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import com.sun.source.doctree.LinkTree;
|
||||
import com.sun.source.util.DocTreePath;
|
||||
|
||||
import jdk.javadoc.doclet.Taglet;
|
||||
import jdk.javadoc.internal.doclets.formats.html.ClassWriterImpl;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlLinkInfo;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.Text;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocLink;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable;
|
||||
|
||||
import static com.sun.source.doctree.DocTree.Kind.LINK;
|
||||
import static com.sun.source.doctree.DocTree.Kind.LINK_PLAIN;
|
||||
import static com.sun.source.doctree.DocTree.Kind.SEE;
|
||||
|
||||
/**
|
||||
* A taglet that represents the {@code {@link ...}} and {@linkplain ...} tags,
|
||||
* with support for links to program elements in {@code @see} and
|
||||
* {@code {@snippet ...}} tags.
|
||||
*/
|
||||
public class LinkTaglet extends BaseTaglet {
|
||||
LinkTaglet(HtmlConfiguration config, DocTree.Kind tagKind) {
|
||||
super(config, tagKind, true, EnumSet.allOf(Taglet.Location.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getInlineTagOutput(Element element, DocTree tag, TagletWriter tagletWriter) {
|
||||
this.tagletWriter = tagletWriter;
|
||||
var linkTree = (LinkTree) tag;
|
||||
var ch = utils.getCommentHelper(element);
|
||||
var context = tagletWriter.context;
|
||||
var htmlWriter = tagletWriter.htmlWriter;
|
||||
|
||||
var inTags = context.inTags;
|
||||
if (inTags.contains(LINK) || inTags.contains(LINK_PLAIN) || inTags.contains(SEE)) {
|
||||
DocTreePath dtp = ch.getDocTreePath(linkTree);
|
||||
if (dtp != null) {
|
||||
messages.warning(dtp, "doclet.see.nested_link", "{@" + linkTree.getTagName() + "}");
|
||||
}
|
||||
Content label = htmlWriter.commentTagsToContent(element, linkTree.getLabel(), context.within(linkTree));
|
||||
if (label.isEmpty()) {
|
||||
label = Text.of(linkTree.getReference().getSignature());
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
var linkRef = linkTree.getReference();
|
||||
if (linkRef == null) {
|
||||
messages.warning(ch.getDocTreePath(tag), "doclet.link.no_reference");
|
||||
return tagletWriter.invalidTagOutput(resources.getText("doclet.tag.invalid_input", tag.toString()),
|
||||
Optional.empty());
|
||||
}
|
||||
|
||||
DocTree.Kind kind = tag.getKind();
|
||||
String refSignature = ch.getReferencedSignature(linkRef);
|
||||
|
||||
return linkSeeReferenceOutput(element,
|
||||
tag,
|
||||
refSignature,
|
||||
ch.getReferencedElement(tag),
|
||||
(kind == LINK_PLAIN),
|
||||
htmlWriter.commentTagsToContent(element, linkTree.getLabel(), context.within(linkTree)),
|
||||
(key, args) -> messages.warning(ch.getDocTreePath(tag), key, args),
|
||||
tagletWriter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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
|
||||
* @param tagletWriter the writer providing the context for this call
|
||||
*
|
||||
* @return the output containing the generated link, or content indicating an error
|
||||
*/
|
||||
Content linkSeeReferenceOutput(Element holder,
|
||||
DocTree refTree,
|
||||
String refSignature,
|
||||
Element ref,
|
||||
boolean isLinkPlain,
|
||||
Content label,
|
||||
BiConsumer<String, Object[]> reportWarning,
|
||||
TagletWriter tagletWriter) {
|
||||
var config = tagletWriter.configuration;
|
||||
var htmlWriter = tagletWriter.htmlWriter;
|
||||
|
||||
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 refFragment = ch.getReferencedFragment(refSignature);
|
||||
|
||||
if (refFragment == null && refMem != null) {
|
||||
refFragment = refMem.toString();
|
||||
} else if (refFragment != null && refFragment.startsWith("#")) {
|
||||
if (labelContent.isEmpty()) {
|
||||
// A non-empty label is required for fragment links as the
|
||||
// reference target does not provide a useful default label.
|
||||
htmlWriter.messages.error(ch.getDocTreePath(refTree), "doclet.link.see.no_label");
|
||||
return tagletWriter.invalidTagOutput(resources.getText("doclet.link.see.no_label"),
|
||||
Optional.of(refSignature));
|
||||
}
|
||||
refFragment = refFragment.substring(1);
|
||||
}
|
||||
if (refClass == null) {
|
||||
ModuleElement refModule = ch.getReferencedModule(ref);
|
||||
if (refModule != null && utils.isIncluded(refModule)) {
|
||||
return htmlWriter.getModuleLink(refModule, labelContent.isEmpty() ? text : labelContent, refFragment);
|
||||
}
|
||||
//@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, refFragment);
|
||||
} 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) :
|
||||
(config.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 (!config.isDocLintReferenceGroupEnabled()) {
|
||||
reportWarning.accept(
|
||||
"doclet.link.see.reference_not_found",
|
||||
new Object[] { refSignature});
|
||||
}
|
||||
return htmlWriter.invalidTagOutput(resources.getText("doclet.link.see.reference_invalid"),
|
||||
Optional.of(labelContent.isEmpty() ? text: labelContent));
|
||||
}
|
||||
}
|
||||
} else if (refFragment == null) {
|
||||
// Must be a class reference since refClass is not null and refFragment 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(config, HtmlLinkInfo.Kind.LINK_TYPE_PARAMS_AND_BOUNDS, referencedType)));
|
||||
}
|
||||
labelContent = plainOrCode(isLinkPlain, Text.of(utils.getSimpleName(refClass)));
|
||||
}
|
||||
return htmlWriter.getLink(new HtmlLinkInfo(config, HtmlLinkInfo.Kind.PLAIN, refClass)
|
||||
.label(labelContent));
|
||||
} else if (refMem == null) {
|
||||
// This is a fragment reference since refClass and refFragment are not null but refMem is null.
|
||||
return htmlWriter.getLink(new HtmlLinkInfo(config, HtmlLinkInfo.Kind.PLAIN, refClass)
|
||||
.label(labelContent)
|
||||
.fragment(refFragment)
|
||||
.style(null));
|
||||
} 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 = config.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 cw) {
|
||||
containing = cw.getTypeElement();
|
||||
} else if (!utils.isPublic(containing)) {
|
||||
reportWarning.accept("doclet.link.see.reference_not_accessible",
|
||||
new Object[] { utils.getFullyQualifiedName(containing)});
|
||||
} else {
|
||||
if (!config.isDocLintReferenceGroupEnabled()) {
|
||||
reportWarning.accept("doclet.link.see.reference_not_found",
|
||||
new Object[] { refSignature });
|
||||
}
|
||||
}
|
||||
}
|
||||
String refMemName = refFragment;
|
||||
if (config.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.SHOW_PREVIEW, 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);
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,7 +23,7 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets;
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
@ -31,29 +31,38 @@ import javax.lang.model.element.Element;
|
||||
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import com.sun.source.doctree.LiteralTree;
|
||||
import jdk.javadoc.doclet.Taglet.Location;
|
||||
|
||||
import jdk.javadoc.doclet.Taglet;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.Text;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
|
||||
/**
|
||||
* An inline taglet used to denote literal code fragments.
|
||||
* The enclosed text is interpreted as not containing HTML markup or
|
||||
* nested javadoc tags, and is rendered in a font suitable for code.
|
||||
* An inline taglet used to denote literal text, possibly in monospace font.
|
||||
*
|
||||
* For example, the text:
|
||||
* <blockquote> {@code {@literal a<B>c}} </blockquote>
|
||||
* displays as:
|
||||
* <blockquote> {@literal a<B>c} </blockquote>
|
||||
*
|
||||
* <p> The tag {@code {@code ...}} is equivalent to
|
||||
* {@code <code>{@literal ...}</code>}.
|
||||
*
|
||||
* For example, the text:
|
||||
* <blockquote> The type {@code {@code List<P>}} </blockquote>
|
||||
* displays as:
|
||||
* <blockquote> The type {@code List<P>} </blockquote>
|
||||
*/
|
||||
public class CodeTaglet extends BaseTaglet {
|
||||
|
||||
CodeTaglet() {
|
||||
super(DocTree.Kind.CODE, true, EnumSet.allOf(Location.class));
|
||||
public class LiteralTaglet extends BaseTaglet {
|
||||
LiteralTaglet(HtmlConfiguration config, DocTree.Kind tagKind) {
|
||||
super(config, tagKind, true, EnumSet.allOf(Taglet.Location.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getInlineTagOutput(Element element, DocTree tag, TagletWriter writer) {
|
||||
return writer.codeTagOutput(element, (LiteralTree) tag);
|
||||
public Content getInlineTagOutput(Element element, DocTree tag, TagletWriter tagletWriter) {
|
||||
var literalTree = (LiteralTree) tag;
|
||||
var body = Text.of(Text.normalizeNewlines(literalTree.getBody().getBody()));
|
||||
return tag.getKind() == DocTree.Kind.CODE ? HtmlTree.CODE(body) : body;
|
||||
}
|
||||
}
|
@ -23,9 +23,13 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets;
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
@ -34,19 +38,24 @@ import javax.lang.model.element.TypeElement;
|
||||
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import com.sun.source.doctree.ParamTree;
|
||||
import jdk.javadoc.doclet.Taglet.Location;
|
||||
import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
|
||||
|
||||
import jdk.javadoc.doclet.Taglet;
|
||||
import jdk.javadoc.internal.doclets.formats.html.Contents;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlIds;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.Text;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Messages;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocFinder;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocFinder.Result;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.Utils;
|
||||
|
||||
/**
|
||||
* A taglet that represents the {@code @param} tag.
|
||||
*/
|
||||
public class ParamTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
|
||||
public enum ParamKind {
|
||||
/** Parameter of an executable element. */
|
||||
PARAMETER,
|
||||
@ -56,15 +65,15 @@ public class ParamTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
TYPE_PARAMETER
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a ParamTaglet.
|
||||
*/
|
||||
public ParamTaglet() {
|
||||
super(DocTree.Kind.PARAM, false, EnumSet.of(Location.TYPE, Location.CONSTRUCTOR, Location.METHOD));
|
||||
private final Contents contents;
|
||||
|
||||
ParamTaglet(HtmlConfiguration config) {
|
||||
super(config, DocTree.Kind.PARAM, false, EnumSet.of(Taglet.Location.TYPE, Taglet.Location.CONSTRUCTOR, Taglet.Location.METHOD));
|
||||
contents = config.contents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Output inherit(Element dst, Element src, DocTree tag, boolean isFirstSentence, BaseConfiguration configuration) {
|
||||
public Output inherit(Element dst, Element src, DocTree tag, boolean isFirstSentence) {
|
||||
assert dst.getKind() == ElementKind.METHOD;
|
||||
assert tag.getKind() == DocTree.Kind.PARAM;
|
||||
var method = (ExecutableElement) dst;
|
||||
@ -76,24 +85,24 @@ public class ParamTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
} else {
|
||||
parameterElements = method.getParameters();
|
||||
}
|
||||
Map<String, Integer> stringIntegerMap = mapNameToPosition(configuration.utils, parameterElements);
|
||||
CommentHelper ch = configuration.utils.getCommentHelper(dst);
|
||||
Map<String, Integer> stringIntegerMap = mapNameToPosition(utils, parameterElements);
|
||||
CommentHelper ch = utils.getCommentHelper(dst);
|
||||
Integer position = stringIntegerMap.get(ch.getParameterName(param));
|
||||
if (position == null) {
|
||||
return new Output(null, null, List.of(), true);
|
||||
}
|
||||
// try to inherit description of the respective parameter in an overridden method
|
||||
try {
|
||||
var docFinder = configuration.utils.docFinder();
|
||||
var docFinder = utils.docFinder();
|
||||
|
||||
Optional<Documentation> r;
|
||||
if (src != null){
|
||||
r = docFinder.search((ExecutableElement) src,
|
||||
m -> Result.fromOptional(extract(configuration.utils, m, position, param.isTypeParameter())))
|
||||
m -> DocFinder.Result.fromOptional(extract(utils, m, position, param.isTypeParameter())))
|
||||
.toOptional();
|
||||
} else {
|
||||
r = docFinder.find((ExecutableElement) dst,
|
||||
m -> Result.fromOptional(extract(configuration.utils, m, position, param.isTypeParameter())))
|
||||
m -> DocFinder.Result.fromOptional(extract(utils, m, position, param.isTypeParameter())))
|
||||
.toOptional();
|
||||
}
|
||||
return r.map(result -> new Output(result.paramTree, result.method, result.paramTree.getDescription(), true))
|
||||
@ -122,21 +131,21 @@ public class ParamTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getAllBlockTagOutput(Element holder, TagletWriter writer) {
|
||||
Utils utils = writer.configuration().utils;
|
||||
public Content getAllBlockTagOutput(Element holder, TagletWriter tagletWriter) {
|
||||
this.tagletWriter = tagletWriter;
|
||||
if (utils.isExecutableElement(holder)) {
|
||||
ExecutableElement member = (ExecutableElement) holder;
|
||||
Content output = convertParams(member, ParamKind.TYPE_PARAMETER,
|
||||
utils.getTypeParamTrees(member), member.getTypeParameters(), writer);
|
||||
utils.getTypeParamTrees(member), member.getTypeParameters(), tagletWriter);
|
||||
output.add(convertParams(member, ParamKind.PARAMETER,
|
||||
utils.getParamTrees(member), member.getParameters(), writer));
|
||||
utils.getParamTrees(member), member.getParameters(), tagletWriter));
|
||||
return output;
|
||||
} else {
|
||||
TypeElement typeElement = (TypeElement) holder;
|
||||
Content output = convertParams(typeElement, ParamKind.TYPE_PARAMETER,
|
||||
utils.getTypeParamTrees(typeElement), typeElement.getTypeParameters(), writer);
|
||||
utils.getTypeParamTrees(typeElement), typeElement.getTypeParameters(), tagletWriter);
|
||||
output.add(convertParams(typeElement, ParamKind.RECORD_COMPONENT,
|
||||
utils.getParamTrees(typeElement), typeElement.getRecordComponents(), writer));
|
||||
utils.getParamTrees(typeElement), typeElement.getRecordComponents(), tagletWriter));
|
||||
return output;
|
||||
}
|
||||
}
|
||||
@ -163,10 +172,9 @@ public class ParamTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
List<? extends Element> parameters,
|
||||
TagletWriter writer) {
|
||||
Map<Integer, ParamTree> tagOfPosition = new HashMap<>();
|
||||
Messages messages = writer.configuration().getMessages();
|
||||
CommentHelper ch = writer.configuration().utils.getCommentHelper(e);
|
||||
CommentHelper ch = utils.getCommentHelper(e);
|
||||
if (!tags.isEmpty()) {
|
||||
Map<String, Integer> positionOfName = mapNameToPosition(writer.configuration().utils, parameters);
|
||||
Map<String, Integer> positionOfName = mapNameToPosition(utils, parameters);
|
||||
for (ParamTree tag : tags) {
|
||||
String name = ch.getParameterName(tag);
|
||||
String paramName = kind == ParamKind.TYPE_PARAMETER ? "<" + name + ">" : name;
|
||||
@ -176,7 +184,7 @@ public class ParamTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
case TYPE_PARAMETER -> "doclet.TypeParameters_warn";
|
||||
case RECORD_COMPONENT -> "doclet.RecordComponents_warn";
|
||||
};
|
||||
if (!writer.configuration().isDocLintReferenceGroupEnabled()) {
|
||||
if (!config.isDocLintReferenceGroupEnabled()) {
|
||||
messages.warning(ch.getDocTreePath(tag), key, paramName);
|
||||
}
|
||||
}
|
||||
@ -188,7 +196,7 @@ public class ParamTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
case TYPE_PARAMETER -> "doclet.TypeParameters_dup_warn";
|
||||
case RECORD_COMPONENT -> "doclet.RecordComponents_dup_warn";
|
||||
};
|
||||
if (!writer.configuration().isDocLintReferenceGroupEnabled()) {
|
||||
if (!config.isDocLintReferenceGroupEnabled()) {
|
||||
messages.warning(ch.getDocTreePath(tag), key, paramName);
|
||||
}
|
||||
} else {
|
||||
@ -205,7 +213,7 @@ public class ParamTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
if (tag != null) {
|
||||
result.add(convertParam(e, kind, writer, tag,
|
||||
ch.getParameterName(tag), result.isEmpty()));
|
||||
} else if (writer.configuration().utils.isMethod(e)) {
|
||||
} else if (utils.isMethod(e)) {
|
||||
result.add(getInheritedTagletOutput(kind, e, writer,
|
||||
parameters.get(i), i, result.isEmpty()));
|
||||
}
|
||||
@ -233,10 +241,9 @@ public class ParamTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
Element param,
|
||||
int position,
|
||||
boolean isFirst) {
|
||||
Utils utils = writer.configuration().utils;
|
||||
Content result = writer.getOutputInstance();
|
||||
var r = utils.docFinder().search((ExecutableElement) holder,
|
||||
m -> Result.fromOptional(extract(utils, m, position, kind == ParamKind.TYPE_PARAMETER)))
|
||||
m -> DocFinder.Result.fromOptional(extract(utils, m, position, kind == ParamTaglet.ParamKind.TYPE_PARAMETER)))
|
||||
.toOptional();
|
||||
if (r.isPresent()) {
|
||||
String name = kind != ParamKind.TYPE_PARAMETER
|
||||
@ -249,6 +256,31 @@ public class ParamTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
return result;
|
||||
}
|
||||
|
||||
private Content getParamHeader(ParamKind kind) {
|
||||
var header = switch (kind) {
|
||||
case PARAMETER -> contents.parameters;
|
||||
case TYPE_PARAMETER -> contents.typeParameters;
|
||||
case RECORD_COMPONENT -> contents.recordComponents;
|
||||
};
|
||||
return HtmlTree.DT(header);
|
||||
}
|
||||
|
||||
private Content paramTagOutput(Element element, ParamTree paramTag, String paramName) {
|
||||
var context = tagletWriter.context;
|
||||
var htmlWriter = tagletWriter.htmlWriter;
|
||||
var body = new ContentBuilder();
|
||||
CommentHelper ch = utils.getCommentHelper(element);
|
||||
// define id attributes for state components so that generated descriptions may refer to them
|
||||
boolean defineID = (element.getKind() == ElementKind.RECORD)
|
||||
&& !paramTag.isTypeParameter();
|
||||
Content nameContent = Text.of(paramName);
|
||||
body.add(HtmlTree.CODE(defineID ? HtmlTree.SPAN_ID(HtmlIds.forParam(paramName), nameContent) : nameContent));
|
||||
body.add(" - ");
|
||||
List<? extends DocTree> description = ch.getDescription(paramTag);
|
||||
body.add(htmlWriter.commentTagsToContent(element, description, context.within(paramTag)));
|
||||
return HtmlTree.DD(body);
|
||||
}
|
||||
|
||||
private record Documentation(ParamTree paramTree, ExecutableElement method) { }
|
||||
|
||||
private static Optional<Documentation> extract(Utils utils, ExecutableElement method, Integer position, boolean typeParam) {
|
||||
@ -276,9 +308,9 @@ public class ParamTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
boolean isFirstParam) {
|
||||
Content result = writer.getOutputInstance();
|
||||
if (isFirstParam) {
|
||||
result.add(writer.getParamHeader(kind));
|
||||
result.add(getParamHeader(kind));
|
||||
}
|
||||
result.add(writer.paramTagOutput(e, paramTag, name));
|
||||
result.add(paramTagOutput(e, paramTag, name));
|
||||
return result;
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets;
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
@ -37,21 +37,25 @@ import javax.lang.model.type.TypeMirror;
|
||||
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import com.sun.source.doctree.ReturnTree;
|
||||
import jdk.javadoc.doclet.Taglet.Location;
|
||||
import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
|
||||
|
||||
import jdk.javadoc.doclet.Taglet;
|
||||
import jdk.javadoc.internal.doclets.formats.html.Contents;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Messages;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocFinder;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocFinder.Result;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.Utils;
|
||||
|
||||
/**
|
||||
* A taglet that represents the {@code @return} and {@code {@return }} tags.
|
||||
*/
|
||||
public class ReturnTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
private final Contents contents;
|
||||
|
||||
public ReturnTaglet() {
|
||||
super(DocTree.Kind.RETURN, true, EnumSet.of(Location.METHOD));
|
||||
ReturnTaglet(HtmlConfiguration config) {
|
||||
super(config, DocTree.Kind.RETURN, true, EnumSet.of(Taglet.Location.METHOD));
|
||||
contents = config.contents;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -60,14 +64,14 @@ public class ReturnTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Output inherit(Element dst, Element src, DocTree tag, boolean isFirstSentence, BaseConfiguration configuration) {
|
||||
public Output inherit(Element dst, Element src, DocTree tag, boolean isFirstSentence) {
|
||||
try {
|
||||
var docFinder = configuration.utils.docFinder();
|
||||
var docFinder = utils.docFinder();
|
||||
Optional<Documentation> r;
|
||||
if (src == null) {
|
||||
r = docFinder.find((ExecutableElement) dst, m -> Result.fromOptional(extract(configuration.utils, m))).toOptional();
|
||||
r = docFinder.find((ExecutableElement) dst, m -> DocFinder.Result.fromOptional(extract(utils, m))).toOptional();
|
||||
} else {
|
||||
r = docFinder.search((ExecutableElement) src, m -> Result.fromOptional(extract(configuration.utils, m))).toOptional();
|
||||
r = docFinder.search((ExecutableElement) src, m -> DocFinder.Result.fromOptional(extract(utils, m))).toOptional();
|
||||
}
|
||||
return r.map(result -> new Output(result.returnTree, result.method, result.returnTree.getDescription(), true))
|
||||
.orElseGet(() -> new Output(null, null, List.of(), true));
|
||||
@ -77,22 +81,22 @@ public class ReturnTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getInlineTagOutput(Element element, DocTree tag, TagletWriter writer) {
|
||||
return writer.returnTagOutput(element, (ReturnTree) tag, true);
|
||||
public Content getInlineTagOutput(Element element, DocTree tag, TagletWriter tagletWriter) {
|
||||
this.tagletWriter = tagletWriter;
|
||||
return returnTagOutput(element, (ReturnTree) tag, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getAllBlockTagOutput(Element holder, TagletWriter writer) {
|
||||
public Content getAllBlockTagOutput(Element holder, TagletWriter tagletWriter) {
|
||||
assert holder.getKind() == ElementKind.METHOD : holder.getKind();
|
||||
var method = (ExecutableElement) holder;
|
||||
Messages messages = writer.configuration().getMessages();
|
||||
Utils utils = writer.configuration().utils;
|
||||
this.tagletWriter = tagletWriter;
|
||||
List<? extends ReturnTree> tags = utils.getReturnTrees(holder);
|
||||
|
||||
// make sure we are not using @return on a method with the void return type
|
||||
TypeMirror returnType = utils.getReturnType(writer.getCurrentPageElement(), method);
|
||||
TypeMirror returnType = utils.getReturnType(tagletWriter.getCurrentPageElement(), method);
|
||||
if (returnType != null && utils.isVoid(returnType)) {
|
||||
if (!tags.isEmpty() && !writer.configuration().isDocLintReferenceGroupEnabled()) {
|
||||
if (!tags.isEmpty() && !config.isDocLintReferenceGroupEnabled()) {
|
||||
messages.warning(holder, "doclet.Return_tag_on_void_method");
|
||||
}
|
||||
return null;
|
||||
@ -103,11 +107,31 @@ public class ReturnTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
// above for a case where @return is used for void
|
||||
|
||||
var docFinder = utils.docFinder();
|
||||
return docFinder.search(method, m -> Result.fromOptional(extract(utils, m))).toOptional()
|
||||
.map(r -> writer.returnTagOutput(r.method, r.returnTree, false))
|
||||
return docFinder.search(method, m -> DocFinder.Result.fromOptional(extract(utils, m))).toOptional()
|
||||
.map(r -> returnTagOutput(r.method, r.returnTree, false))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the output for a {@code @return} tag.
|
||||
*
|
||||
* @param element the element that owns the doc comment
|
||||
* @param returnTag the return tag to document
|
||||
* @param inline whether this should be written as an inline instance or block instance
|
||||
*
|
||||
* @return the output
|
||||
*/
|
||||
public Content returnTagOutput(Element element, ReturnTree returnTag, boolean inline) {
|
||||
var context = tagletWriter.context;
|
||||
var htmlWriter = tagletWriter.htmlWriter;
|
||||
var ch = utils.getCommentHelper(element);
|
||||
List<? extends DocTree> desc = ch.getDescription(returnTag);
|
||||
Content content = htmlWriter.commentTagsToContent(element, desc, context.within(returnTag));
|
||||
return inline
|
||||
? new ContentBuilder(contents.getContent("doclet.Returns_0", content))
|
||||
: new ContentBuilder(HtmlTree.DT(contents.returns), HtmlTree.DD(content));
|
||||
}
|
||||
|
||||
private record Documentation(ReturnTree returnTree, ExecutableElement method) { }
|
||||
|
||||
private static Optional<Documentation> extract(Utils utils, ExecutableElement method) {
|
@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import com.sun.source.doctree.SeeTree;
|
||||
|
||||
import jdk.javadoc.doclet.Taglet;
|
||||
import jdk.javadoc.internal.doclets.formats.html.ClassWriterImpl;
|
||||
import jdk.javadoc.internal.doclets.formats.html.Contents;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlDocletWriter;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.Text;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
import jdk.javadoc.internal.doclets.toolkit.builders.SerializedFormBuilder;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocFinder;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocLink;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.Utils;
|
||||
|
||||
public class SeeTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
SeeTaglet(HtmlConfiguration config) {
|
||||
super(config, DocTree.Kind.SEE, false, EnumSet.allOf(Taglet.Location.class));
|
||||
contents = config.contents;
|
||||
}
|
||||
|
||||
private final Contents contents;
|
||||
private HtmlDocletWriter htmlWriter;
|
||||
|
||||
|
||||
@Override
|
||||
public Output inherit(Element dst, Element src, DocTree tag, boolean isFirstSentence) {
|
||||
CommentHelper ch = utils.getCommentHelper(dst);
|
||||
var path = ch.getDocTreePath(tag);
|
||||
messages.warning(path, "doclet.inheritDocWithinInappropriateTag");
|
||||
return new Output(null, null, List.of(), true /* true, otherwise there will be an exception up the stack */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getAllBlockTagOutput(Element holder, TagletWriter tagletWriter) {
|
||||
this.tagletWriter = tagletWriter;
|
||||
List<? extends SeeTree> tags = utils.getSeeTrees(holder);
|
||||
Element e = holder;
|
||||
if (utils.isMethod(holder)) {
|
||||
var docFinder = utils.docFinder();
|
||||
Optional<Documentation> result = docFinder.search((ExecutableElement) holder,
|
||||
m -> DocFinder.Result.fromOptional(extract(utils, m))).toOptional();
|
||||
if (result.isPresent()) {
|
||||
ExecutableElement m = result.get().method();
|
||||
tags = utils.getSeeTrees(m);
|
||||
e = m;
|
||||
}
|
||||
}
|
||||
return seeTagOutput(e, tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the output for {@code @see} tags.
|
||||
*
|
||||
* @param holder The element that owns the doc comment
|
||||
* @param seeTags the list of tags
|
||||
*
|
||||
* @return the output
|
||||
*/
|
||||
public Content seeTagOutput(Element holder, List<? extends SeeTree> seeTags) {
|
||||
htmlWriter = tagletWriter.htmlWriter;
|
||||
|
||||
List<Content> links = new ArrayList<>();
|
||||
for (SeeTree dt : seeTags) {
|
||||
links.add(seeTagOutput(holder, dt));
|
||||
}
|
||||
if (utils.isVariableElement(holder) && ((VariableElement)holder).getConstantValue() != null &&
|
||||
htmlWriter instanceof ClassWriterImpl classWriter) {
|
||||
//Automatically add link to constant values page for constant fields.
|
||||
DocPath constantsPath =
|
||||
htmlWriter.pathToRoot.resolve(DocPaths.CONSTANT_VALUES);
|
||||
String whichConstant =
|
||||
classWriter.getTypeElement().getQualifiedName() + "." +
|
||||
utils.getSimpleName(holder);
|
||||
DocLink link = constantsPath.fragment(whichConstant);
|
||||
links.add(htmlWriter.links.createLink(link,
|
||||
contents.getContent("doclet.Constants_Summary")));
|
||||
}
|
||||
if (utils.isClass(holder) && utils.isSerializable((TypeElement)holder)) {
|
||||
//Automatically add link to serialized form page for serializable classes.
|
||||
if (SerializedFormBuilder.serialInclude(utils, holder) &&
|
||||
SerializedFormBuilder.serialInclude(utils, utils.containingPackage(holder))) {
|
||||
DocPath serialPath = htmlWriter.pathToRoot.resolve(DocPaths.SERIALIZED_FORM);
|
||||
DocLink link = serialPath.fragment(utils.getFullyQualifiedName(holder));
|
||||
links.add(htmlWriter.links.createLink(link,
|
||||
contents.getContent("doclet.Serialized_Form")));
|
||||
}
|
||||
}
|
||||
if (links.isEmpty()) {
|
||||
return Text.EMPTY;
|
||||
}
|
||||
|
||||
var seeList = tagletWriter.tagList(links);
|
||||
return new ContentBuilder(
|
||||
HtmlTree.DT(contents.seeAlso),
|
||||
HtmlTree.DD(seeList));
|
||||
}
|
||||
|
||||
private record Documentation(List<? extends SeeTree> seeTrees, ExecutableElement method) { }
|
||||
|
||||
private static Optional<Documentation> extract(Utils utils, ExecutableElement method) {
|
||||
List<? extends SeeTree> tags = utils.getSeeTrees(method);
|
||||
return tags.isEmpty() ? Optional.empty() : Optional.of(new Documentation(tags, method));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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 refSignature = ch.getReferencedSignature(ref0);
|
||||
List<? extends DocTree> label = ref.subList(1, ref.size());
|
||||
|
||||
var lt = (LinkTaglet) config.tagletManager.getTaglet(DocTree.Kind.LINK);
|
||||
return lt.linkSeeReferenceOutput(element,
|
||||
seeTag,
|
||||
refSignature,
|
||||
ch.getReferencedElement(seeTag),
|
||||
false,
|
||||
htmlWriter.commentTagsToContent(element, label, tagletWriter.getContext().within(seeTag)),
|
||||
(key, args) -> messages.warning(ch.getDocTreePath(seeTag), key, args),
|
||||
tagletWriter
|
||||
);
|
||||
}
|
||||
|
||||
case ERRONEOUS -> {
|
||||
return tagletWriter.invalidTagOutput(resources.getText("doclet.tag.invalid_input",
|
||||
ref0.toString()),
|
||||
Optional.empty());
|
||||
}
|
||||
|
||||
default -> throw new IllegalStateException(ref0.getKind().toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -23,7 +23,7 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets;
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
@ -34,14 +34,17 @@ import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
|
||||
import com.sun.source.doctree.BlockTagTree;
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import jdk.javadoc.doclet.Taglet.Location;
|
||||
import com.sun.source.doctree.UnknownBlockTagTree;
|
||||
|
||||
import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
|
||||
import jdk.javadoc.doclet.Taglet;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.RawHtml;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocFinder;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocFinder.Result;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.Utils;
|
||||
|
||||
/**
|
||||
* A custom single-argument block tag.
|
||||
@ -51,15 +54,9 @@ public class SimpleTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
/**
|
||||
* The header to output.
|
||||
*/
|
||||
protected String header;
|
||||
private final String header;
|
||||
|
||||
/**
|
||||
* Whether or not the taglet should generate output.
|
||||
* Standard tags like {@code @author}, {@code @since}, {@code @version} can
|
||||
* be disabled by command-line options; custom tags created with -tag can be
|
||||
* disabled with an X in the defining string.
|
||||
*/
|
||||
protected final boolean enabled;
|
||||
private final boolean enabled;
|
||||
|
||||
/**
|
||||
* Constructs a {@code SimpleTaglet}.
|
||||
@ -72,8 +69,8 @@ public class SimpleTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
* See {@link #getLocations(String) getLocations} for the
|
||||
* complete list.
|
||||
*/
|
||||
public SimpleTaglet(String tagName, String header, String locations) {
|
||||
this(tagName, header, getLocations(locations), isEnabled(locations));
|
||||
SimpleTaglet(HtmlConfiguration config, String tagName, String header, String locations) {
|
||||
this(config, tagName, header, getLocations(locations), isEnabled(locations));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -83,8 +80,8 @@ public class SimpleTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
* @param header the header to output
|
||||
* @param locations the possible locations that this tag can appear in
|
||||
*/
|
||||
public SimpleTaglet(DocTree.Kind tagKind, String header, Set<Location> locations) {
|
||||
this(tagKind, header, locations, true);
|
||||
SimpleTaglet(HtmlConfiguration config, DocTree.Kind tagKind, String header, Set<Taglet.Location> locations) {
|
||||
this(config, tagKind, header, locations, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -94,8 +91,8 @@ public class SimpleTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
* @param header the header to output
|
||||
* @param locations the possible locations that this tag can appear in
|
||||
*/
|
||||
public SimpleTaglet(String tagName, String header, Set<Location> locations) {
|
||||
this(tagName, header, locations, true);
|
||||
SimpleTaglet(HtmlConfiguration config, String tagName, String header, Set<Taglet.Location> locations) {
|
||||
this(config, tagName, header, locations, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -105,8 +102,8 @@ public class SimpleTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
* @param header the header to output
|
||||
* @param locations the possible locations that this tag can appear in
|
||||
*/
|
||||
public SimpleTaglet(String tagName, String header, Set<Location> locations, boolean enabled) {
|
||||
super(tagName, false, locations);
|
||||
private SimpleTaglet(HtmlConfiguration config, String tagName, String header, Set<Taglet.Location> locations, boolean enabled) {
|
||||
super(config, tagName, false, locations);
|
||||
this.header = header;
|
||||
this.enabled = enabled;
|
||||
}
|
||||
@ -118,38 +115,136 @@ public class SimpleTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
* @param header the header to output
|
||||
* @param locations the possible locations that this tag can appear in
|
||||
*/
|
||||
public SimpleTaglet(DocTree.Kind tagKind, String header, Set<Location> locations, boolean enabled) {
|
||||
super(tagKind, false, locations);
|
||||
protected SimpleTaglet(HtmlConfiguration config, DocTree.Kind tagKind, String header, Set<Taglet.Location> locations, boolean enabled) {
|
||||
super(config, tagKind, false, locations);
|
||||
this.header = header;
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
private static Set<Location> getLocations(String locations) {
|
||||
Set<Location> set = EnumSet.noneOf(Location.class);
|
||||
@Override
|
||||
public Output inherit(Element dst, Element src, DocTree tag, boolean isFirstSentence) {
|
||||
assert dst.getKind() == ElementKind.METHOD;
|
||||
assert !isFirstSentence;
|
||||
try {
|
||||
var docFinder = utils.docFinder();
|
||||
Optional<Documentation> r;
|
||||
if (src == null) {
|
||||
r = docFinder.find((ExecutableElement) dst,
|
||||
m -> DocFinder.Result.fromOptional(extractFirst(m))).toOptional();
|
||||
} else {
|
||||
r = docFinder.search((ExecutableElement) src,
|
||||
m -> DocFinder.Result.fromOptional(extractFirst(m))).toOptional();
|
||||
}
|
||||
return r.map(result -> new Output(result.tag, result.method, result.description, true))
|
||||
.orElseGet(()->new Output(null, null, List.of(), true));
|
||||
} catch (DocFinder.NoOverriddenMethodFound e) {
|
||||
return new Output(null, null, List.of(), false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the taglet should generate output.
|
||||
* Standard tags like {@code @author}, {@code @since}, {@code @version} can
|
||||
* be disabled by command-line options; custom tags created with -tag can be
|
||||
* disabled with an X in the defining string.
|
||||
*/
|
||||
boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this taglet accepts a {@code BlockTagTree} node.
|
||||
* The taglet accepts a tree node if it has the same kind, and
|
||||
* if the kind is {@code UNKNOWN_BLOCK_TAG} the same tag name.
|
||||
*
|
||||
* @param tree the tree node
|
||||
* @return {@code true} if this taglet accepts this tree node
|
||||
*/
|
||||
private boolean accepts(BlockTagTree tree) {
|
||||
return (tree.getKind() == DocTree.Kind.UNKNOWN_BLOCK_TAG && tagKind == DocTree.Kind.UNKNOWN_BLOCK_TAG)
|
||||
? tree.getTagName().equals(name)
|
||||
: tree.getKind() == tagKind;
|
||||
}
|
||||
|
||||
record Documentation(DocTree tag, List<? extends DocTree> description, ExecutableElement method) { }
|
||||
|
||||
private Optional<Documentation> extractFirst(ExecutableElement m) {
|
||||
List<? extends DocTree> tags = utils.getBlockTags(m, this::accepts);
|
||||
if (tags.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
DocTree t = tags.get(0);
|
||||
return Optional.of(new Documentation(t, utils.getCommentHelper(m).getDescription(t), m));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getAllBlockTagOutput(Element holder, TagletWriter tagletWriter) {
|
||||
this.tagletWriter = tagletWriter;
|
||||
List<? extends DocTree> tags = utils.getBlockTags(holder, this::accepts);
|
||||
if (header == null || tags.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return simpleBlockTagOutput(holder, tags, header, tagletWriter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the output for a series of simple tags.
|
||||
*
|
||||
* @param element The element that owns the doc comment
|
||||
* @param simpleTags the list of simple tags
|
||||
* @param header the header for the series of tags
|
||||
*
|
||||
* @return the output
|
||||
*/
|
||||
private Content simpleBlockTagOutput(Element element,
|
||||
List<? extends DocTree> simpleTags,
|
||||
String header,
|
||||
TagletWriter writer) {
|
||||
var ch = utils.getCommentHelper(element);
|
||||
var context = tagletWriter.context;
|
||||
var htmlWriter = tagletWriter.htmlWriter;
|
||||
|
||||
ContentBuilder body = new ContentBuilder();
|
||||
boolean many = false;
|
||||
for (DocTree simpleTag : simpleTags) {
|
||||
if (many) {
|
||||
body.add(", ");
|
||||
}
|
||||
List<? extends DocTree> bodyTags = ch.getBody(simpleTag);
|
||||
body.add(htmlWriter.commentTagsToContent(element, bodyTags, context.within(simpleTag)));
|
||||
many = true;
|
||||
}
|
||||
return new ContentBuilder(
|
||||
HtmlTree.DT(RawHtml.of(header)),
|
||||
HtmlTree.DD(body));
|
||||
}
|
||||
|
||||
private static Set<Taglet.Location> getLocations(String locations) {
|
||||
Set<Taglet.Location> set = EnumSet.noneOf(Taglet.Location.class);
|
||||
for (int i = 0; i < locations.length(); i++) {
|
||||
switch (locations.charAt(i)) {
|
||||
case 'a': case 'A':
|
||||
return EnumSet.allOf(Location.class);
|
||||
return EnumSet.allOf(Taglet.Location.class);
|
||||
case 'c': case 'C':
|
||||
set.add(Location.CONSTRUCTOR);
|
||||
set.add(Taglet.Location.CONSTRUCTOR);
|
||||
break;
|
||||
case 'f': case 'F':
|
||||
set.add(Location.FIELD);
|
||||
set.add(Taglet.Location.FIELD);
|
||||
break;
|
||||
case 'm': case 'M':
|
||||
set.add(Location.METHOD);
|
||||
set.add(Taglet.Location.METHOD);
|
||||
break;
|
||||
case 'o': case 'O':
|
||||
set.add(Location.OVERVIEW);
|
||||
set.add(Taglet.Location.OVERVIEW);
|
||||
break;
|
||||
case 'p': case 'P':
|
||||
set.add(Location.PACKAGE);
|
||||
set.add(Taglet.Location.PACKAGE);
|
||||
break;
|
||||
case 's': case 'S': // super-packages, anyone?
|
||||
set.add(Location.MODULE);
|
||||
set.add(Taglet.Location.MODULE);
|
||||
break;
|
||||
case 't': case 'T':
|
||||
set.add(Location.TYPE);
|
||||
set.add(Taglet.Location.TYPE);
|
||||
break;
|
||||
case 'x': case 'X':
|
||||
break;
|
||||
@ -161,46 +256,4 @@ public class SimpleTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
private static boolean isEnabled(String locations) {
|
||||
return locations.matches("[^Xx]*");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Output inherit(Element dst, Element src, DocTree tag, boolean isFirstSentence, BaseConfiguration configuration) {
|
||||
assert dst.getKind() == ElementKind.METHOD;
|
||||
assert !isFirstSentence;
|
||||
try {
|
||||
var docFinder = configuration.utils.docFinder();
|
||||
Optional<Documentation> r;
|
||||
if (src == null) {
|
||||
r = docFinder.find((ExecutableElement) dst,
|
||||
m -> Result.fromOptional(extractFirst(m, configuration.utils))).toOptional();
|
||||
} else {
|
||||
r = docFinder.search((ExecutableElement) src,
|
||||
m -> Result.fromOptional(extractFirst(m, configuration.utils))).toOptional();
|
||||
}
|
||||
return r.map(result -> new Output(result.tag, result.method, result.description, true))
|
||||
.orElseGet(()->new Output(null, null, List.of(), true));
|
||||
} catch (DocFinder.NoOverriddenMethodFound e) {
|
||||
return new Output(null, null, List.of(), false);
|
||||
}
|
||||
}
|
||||
|
||||
record Documentation(DocTree tag, List<? extends DocTree> description, ExecutableElement method) { }
|
||||
|
||||
private Optional<Documentation> extractFirst(ExecutableElement m, Utils utils) {
|
||||
List<? extends DocTree> tags = utils.getBlockTags(m, this);
|
||||
if (tags.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
DocTree t = tags.get(0);
|
||||
return Optional.of(new Documentation(t, utils.getCommentHelper(m).getDescription(t), m));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getAllBlockTagOutput(Element holder, TagletWriter writer) {
|
||||
Utils utils = writer.configuration().utils;
|
||||
List<? extends DocTree> tags = utils.getBlockTags(holder, this);
|
||||
if (header == null || tags.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return writer.simpleBlockTagOutput(holder, tags, header);
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,34 +23,46 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets;
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.PackageElement;
|
||||
import javax.tools.Diagnostic;
|
||||
import javax.tools.DocumentationTool.Location;
|
||||
import javax.tools.DocumentationTool;
|
||||
import javax.tools.FileObject;
|
||||
|
||||
import com.sun.source.doctree.AttributeTree;
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import com.sun.source.doctree.SnippetTree;
|
||||
import com.sun.source.doctree.TextTree;
|
||||
import com.sun.source.util.DocTreePath;
|
||||
|
||||
import jdk.javadoc.doclet.Taglet;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.TagName;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.Text;
|
||||
import jdk.javadoc.internal.doclets.formats.html.taglets.snippet.Action;
|
||||
import jdk.javadoc.internal.doclets.formats.html.taglets.snippet.ParseException;
|
||||
import jdk.javadoc.internal.doclets.formats.html.taglets.snippet.Parser;
|
||||
import jdk.javadoc.internal.doclets.formats.html.taglets.snippet.Style;
|
||||
import jdk.javadoc.internal.doclets.formats.html.taglets.snippet.StyledText;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
import jdk.javadoc.internal.doclets.toolkit.DocletElement;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Resources;
|
||||
import jdk.javadoc.internal.doclets.toolkit.taglets.snippet.Action;
|
||||
import jdk.javadoc.internal.doclets.toolkit.taglets.snippet.ParseException;
|
||||
import jdk.javadoc.internal.doclets.toolkit.taglets.snippet.Parser;
|
||||
import jdk.javadoc.internal.doclets.toolkit.taglets.snippet.StyledText;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.Utils;
|
||||
|
||||
/**
|
||||
@ -90,8 +102,8 @@ public class SnippetTaglet extends BaseTaglet {
|
||||
public String getIdentifier() {return identifier;}
|
||||
}
|
||||
|
||||
public SnippetTaglet() {
|
||||
super(DocTree.Kind.SNIPPET, true, EnumSet.allOf(Taglet.Location.class));
|
||||
SnippetTaglet(HtmlConfiguration config) {
|
||||
super(config, DocTree.Kind.SNIPPET, true, EnumSet.allOf(Taglet.Location.class));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -109,16 +121,116 @@ public class SnippetTaglet extends BaseTaglet {
|
||||
* one of the named regions in the snippets content.
|
||||
*/
|
||||
@Override
|
||||
public Content getInlineTagOutput(Element holder, DocTree tag, TagletWriter writer) {
|
||||
public Content getInlineTagOutput(Element holder, DocTree tag, TagletWriter tagletWriter) {
|
||||
this.tagletWriter = tagletWriter;
|
||||
try {
|
||||
return generateContent(holder, tag, writer);
|
||||
return generateContent(holder, tag);
|
||||
} catch (BadSnippetException e) {
|
||||
error(writer, holder, e.tag(), e.key(), e.args());
|
||||
String details = writer.configuration().getDocResources().getText(e.key(), e.args());
|
||||
return badSnippet(writer, Optional.of(details));
|
||||
error(tagletWriter, holder, e.tag(), e.key(), e.args());
|
||||
String details = config.getDocResources().getText(e.key(), e.args());
|
||||
return badSnippet(tagletWriter, Optional.of(details));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the output for a {@code {@snippet ...}} tag.
|
||||
*
|
||||
* @param element The element that owns the doc comment
|
||||
* @param tag the snippet tag
|
||||
* @param id the value of the id attribute, or null if not defined
|
||||
* @param lang the value of the lang attribute, or null if not defined
|
||||
*
|
||||
* @return the output
|
||||
*/
|
||||
private Content snippetTagOutput(Element element, SnippetTree tag, StyledText content,
|
||||
String id, String lang) {
|
||||
var pathToRoot = tagletWriter.htmlWriter.pathToRoot;
|
||||
var pre = new HtmlTree(TagName.PRE).setStyle(HtmlStyle.snippet);
|
||||
if (id != null && !id.isBlank()) {
|
||||
pre.put(HtmlAttr.ID, id);
|
||||
}
|
||||
var code = new HtmlTree(TagName.CODE)
|
||||
.addUnchecked(Text.EMPTY); // Make sure the element is always rendered
|
||||
if (lang != null && !lang.isBlank()) {
|
||||
code.addStyle("language-" + lang);
|
||||
}
|
||||
|
||||
content.consumeBy((styles, sequence) -> {
|
||||
CharSequence text = Text.normalizeNewlines(sequence);
|
||||
if (styles.isEmpty()) {
|
||||
code.add(text);
|
||||
} else {
|
||||
Element e = null;
|
||||
String t = null;
|
||||
boolean linkEncountered = false;
|
||||
boolean markupEncountered = false;
|
||||
Set<String> classes = new HashSet<>();
|
||||
for (Style s : styles) {
|
||||
if (s instanceof Style.Name n) {
|
||||
classes.add(n.name());
|
||||
} else if (s instanceof Style.Link l) {
|
||||
assert !linkEncountered; // TODO: do not assert; pick the first link report on subsequent
|
||||
linkEncountered = true;
|
||||
t = l.target();
|
||||
e = getLinkedElement(element, t);
|
||||
if (e == null) {
|
||||
// TODO: diagnostic output
|
||||
}
|
||||
} else if (s instanceof Style.Markup) {
|
||||
markupEncountered = true;
|
||||
break;
|
||||
} else {
|
||||
// TODO: transform this if...else into an exhaustive
|
||||
// switch over the sealed Style hierarchy when "Pattern
|
||||
// Matching for switch" has been implemented (JEP 406
|
||||
// and friends)
|
||||
throw new AssertionError(styles);
|
||||
}
|
||||
}
|
||||
Content c;
|
||||
if (markupEncountered) {
|
||||
return;
|
||||
} else if (linkEncountered) {
|
||||
assert e != null;
|
||||
//disable preview tagging inside the snippets:
|
||||
Utils.PreviewFlagProvider prevPreviewProvider = utils.setPreviewFlagProvider(el -> false);
|
||||
try {
|
||||
var lt = (LinkTaglet) config.tagletManager.getTaglet(DocTree.Kind.LINK);
|
||||
c = lt.linkSeeReferenceOutput(element,
|
||||
null,
|
||||
t,
|
||||
e,
|
||||
false, // TODO: for now
|
||||
Text.of(sequence.toString()),
|
||||
(key, args) -> { /* TODO: report diagnostic */ },
|
||||
tagletWriter);
|
||||
} finally {
|
||||
utils.setPreviewFlagProvider(prevPreviewProvider);
|
||||
}
|
||||
} else {
|
||||
c = HtmlTree.SPAN(Text.of(text));
|
||||
classes.forEach(((HtmlTree) c)::addStyle);
|
||||
}
|
||||
code.add(c);
|
||||
}
|
||||
});
|
||||
String copyText = resources.getText("doclet.Copy_to_clipboard");
|
||||
String copiedText = resources.getText("doclet.Copied_to_clipboard");
|
||||
String copySnippetText = resources.getText("doclet.Copy_snippet_to_clipboard");
|
||||
var snippetContainer = HtmlTree.DIV(HtmlStyle.snippetContainer,
|
||||
new HtmlTree(TagName.BUTTON)
|
||||
.add(HtmlTree.SPAN(Text.of(copyText))
|
||||
.put(HtmlAttr.DATA_COPIED, copiedText))
|
||||
.add(new HtmlTree(TagName.IMG)
|
||||
.put(HtmlAttr.SRC, pathToRoot.resolve(DocPaths.CLIPBOARD_SVG).getPath())
|
||||
.put(HtmlAttr.ALT, copySnippetText))
|
||||
.addStyle(HtmlStyle.copy)
|
||||
.addStyle(HtmlStyle.snippetCopy)
|
||||
.put(HtmlAttr.ARIA_LABEL, copySnippetText)
|
||||
.put(HtmlAttr.ONCLICK, "copySnippet(this)"));
|
||||
return snippetContainer.add(pre.add(code));
|
||||
}
|
||||
|
||||
private static final class BadSnippetException extends Exception {
|
||||
|
||||
@java.io.Serial
|
||||
@ -147,7 +259,7 @@ public class SnippetTaglet extends BaseTaglet {
|
||||
}
|
||||
}
|
||||
|
||||
private Content generateContent(Element holder, DocTree tag, TagletWriter writer)
|
||||
private Content generateContent(Element holder, DocTree tag)
|
||||
throws BadSnippetException
|
||||
{
|
||||
SnippetTree snippetTag = (SnippetTree) tag;
|
||||
@ -212,11 +324,10 @@ public class SnippetTaglet extends BaseTaglet {
|
||||
}
|
||||
|
||||
// we didn't create JavaFileManager, so we won't close it; even if an error occurs
|
||||
var fileManager = writer.configuration().getFileManager();
|
||||
var fileManager = config.getFileManager();
|
||||
|
||||
try {
|
||||
// first, look in local snippet-files subdirectory
|
||||
var utils = writer.configuration().utils;
|
||||
var pkg = getPackageElement(holder, utils);
|
||||
var pkgLocation = utils.getLocationForPackage(pkg);
|
||||
var pkgName = pkg.getQualifiedName().toString(); // note: empty string for unnamed package
|
||||
@ -224,8 +335,8 @@ public class SnippetTaglet extends BaseTaglet {
|
||||
fileObject = fileManager.getFileForInput(pkgLocation, pkgName, relativeName);
|
||||
|
||||
// if not found in local snippet-files directory, look on snippet path
|
||||
if (fileObject == null && fileManager.hasLocation(Location.SNIPPET_PATH)) {
|
||||
fileObject = fileManager.getFileForInput(Location.SNIPPET_PATH, "", v);
|
||||
if (fileObject == null && fileManager.hasLocation(DocumentationTool.Location.SNIPPET_PATH)) {
|
||||
fileObject = fileManager.getFileForInput(DocumentationTool.Location.SNIPPET_PATH, "", v);
|
||||
}
|
||||
} catch (IOException | IllegalArgumentException e) { // TODO: test this when JDK-8276892 is integrated
|
||||
// JavaFileManager.getFileForInput can throw IllegalArgumentException in certain cases
|
||||
@ -265,36 +376,35 @@ public class SnippetTaglet extends BaseTaglet {
|
||||
|
||||
try {
|
||||
Diags d = (text, pos) -> {
|
||||
var path = writer.configuration().utils.getCommentHelper(holder)
|
||||
var path = utils.getCommentHelper(holder)
|
||||
.getDocTreePath(snippetTag.getBody());
|
||||
writer.configuration().getReporter().print(Diagnostic.Kind.WARNING,
|
||||
config.getReporter().print(Diagnostic.Kind.WARNING,
|
||||
path, pos, pos, pos, text);
|
||||
};
|
||||
if (inlineContent != null) {
|
||||
inlineSnippet = parse(writer.configuration().getDocResources(), d, language, inlineContent);
|
||||
inlineSnippet = parse(resources, d, language, inlineContent);
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
var path = writer.configuration().utils.getCommentHelper(holder)
|
||||
var path = utils.getCommentHelper(holder)
|
||||
.getDocTreePath(snippetTag.getBody());
|
||||
// TODO: there should be a method in Messages; that method should mirror Reporter's; use that method instead accessing Reporter.
|
||||
String msg = writer.configuration().getDocResources()
|
||||
.getText("doclet.snippet.markup", e.getMessage());
|
||||
writer.configuration().getReporter().print(Diagnostic.Kind.ERROR,
|
||||
String msg = resources.getText("doclet.snippet.markup", e.getMessage());
|
||||
config.getReporter().print(Diagnostic.Kind.ERROR,
|
||||
path, e.getPosition(), e.getPosition(), e.getPosition(), msg);
|
||||
return badSnippet(writer, Optional.of(e.getMessage()));
|
||||
return badSnippet(tagletWriter, Optional.of(e.getMessage()));
|
||||
}
|
||||
|
||||
try {
|
||||
var finalFileObject = fileObject;
|
||||
Diags d = (text, pos) -> writer.configuration().getMessages().warning(finalFileObject, pos, pos, pos, text);
|
||||
Diags d = (text, pos) -> messages.warning(finalFileObject, pos, pos, pos, text);
|
||||
if (externalContent != null) {
|
||||
externalSnippet = parse(writer.configuration().getDocResources(), d, language, externalContent);
|
||||
externalSnippet = parse(resources, d, language, externalContent);
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
assert fileObject != null;
|
||||
writer.configuration().getMessages().error(fileObject, e.getPosition(),
|
||||
messages.error(fileObject, e.getPosition(),
|
||||
e.getPosition(), e.getPosition(), "doclet.snippet.markup", e.getMessage());
|
||||
return badSnippet(writer, Optional.of(e.getMessage()));
|
||||
return badSnippet(tagletWriter, Optional.of(e.getMessage()));
|
||||
}
|
||||
|
||||
// the region must be matched at least in one content: it can be matched
|
||||
@ -343,7 +453,7 @@ public class SnippetTaglet extends BaseTaglet {
|
||||
? null
|
||||
: stringValueOf(idAttr);
|
||||
|
||||
return writer.snippetTagOutput(holder, snippetTag, text, id, lang);
|
||||
return snippetTagOutput(holder, snippetTag, text, id, lang);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -382,10 +492,10 @@ public class SnippetTaglet extends BaseTaglet {
|
||||
at.getName().toString());
|
||||
}
|
||||
return at.getValue().stream()
|
||||
// value consists of TextTree or ErroneousTree nodes;
|
||||
// ErroneousTree is a subtype of TextTree
|
||||
.map(t -> ((TextTree) t).getBody())
|
||||
.collect(Collectors.joining());
|
||||
// value consists of TextTree or ErroneousTree nodes;
|
||||
// ErroneousTree is a subtype of TextTree
|
||||
.map(t -> ((TextTree) t).getBody())
|
||||
.collect(Collectors.joining());
|
||||
}
|
||||
|
||||
private String languageFromFileName(String fileName) {
|
||||
@ -399,12 +509,11 @@ public class SnippetTaglet extends BaseTaglet {
|
||||
}
|
||||
|
||||
private void error(TagletWriter writer, Element holder, DocTree tag, String key, Object... args) {
|
||||
writer.configuration().getMessages().error(
|
||||
writer.configuration().utils.getCommentHelper(holder).getDocTreePath(tag), key, args);
|
||||
messages.error(utils.getCommentHelper(holder).getDocTreePath(tag), key, args);
|
||||
}
|
||||
|
||||
private Content badSnippet(TagletWriter writer, Optional<String> details) {
|
||||
Resources resources = writer.configuration().getDocResources();
|
||||
var resources = config.getDocResources();
|
||||
return writer.invalidTagOutput(resources.getText("doclet.tag.invalid", "snippet"), details);
|
||||
}
|
||||
|
||||
@ -461,4 +570,21 @@ public class SnippetTaglet extends BaseTaglet {
|
||||
// The most trivial example of such a string is " ". In fact, any string
|
||||
// with a trailing non-empty blank line would do.
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the element that is linked from the context of the referrer using
|
||||
* the provided signature; returns null if such element could not be found.
|
||||
*
|
||||
* This method is to be used when it is the target of the link that is
|
||||
* important, not the container of the link (e.g. was it an @see,
|
||||
* @link/@linkplain or @snippet tags, etc.)
|
||||
*/
|
||||
private Element getLinkedElement(Element referer, String signature) {
|
||||
var factory = utils.docTrees.getDocTreeFactory();
|
||||
var docCommentTree = utils.getDocCommentTree(referer);
|
||||
var rootPath = new DocTreePath(utils.getTreePath(referer), docCommentTree);
|
||||
var reference = factory.newReferenceTree(signature);
|
||||
var fabricatedPath = new DocTreePath(rootPath, reference);
|
||||
return utils.docTrees.getElement(fabricatedPath);
|
||||
}
|
||||
}
|
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import com.sun.source.doctree.SpecTree;
|
||||
import com.sun.source.doctree.TextTree;
|
||||
import com.sun.source.util.DocTreePath;
|
||||
|
||||
import jdk.javadoc.doclet.Taglet;
|
||||
import jdk.javadoc.internal.doclets.formats.html.Contents;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.Text;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocFinder;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.Utils;
|
||||
|
||||
/**
|
||||
* A taglet that represents the {@code @spec} tag.
|
||||
*/
|
||||
public class SpecTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
private final Contents contents;
|
||||
|
||||
SpecTaglet(HtmlConfiguration config) {
|
||||
super(config, DocTree.Kind.SPEC, false, EnumSet.allOf(Taglet.Location.class));
|
||||
this.contents = config.contents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Output inherit(Element dst, Element src, DocTree tag, boolean isFirstSentence) {
|
||||
CommentHelper ch = utils.getCommentHelper(dst);
|
||||
var path = ch.getDocTreePath(tag);
|
||||
messages.warning(path, "doclet.inheritDocWithinInappropriateTag");
|
||||
return new Output(null, null, List.of(), true /* true, otherwise there will be an exception up the stack */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getAllBlockTagOutput(Element holder, TagletWriter tagletWriter) {
|
||||
this.tagletWriter = tagletWriter;
|
||||
List<? extends SpecTree> tags = utils.getSpecTrees(holder);
|
||||
Element e = holder;
|
||||
if (utils.isMethod(holder)) {
|
||||
var docFinder = utils.docFinder();
|
||||
Optional<Documentation> result = docFinder.search((ExecutableElement) holder,
|
||||
m -> DocFinder.Result.fromOptional(extract(utils, m))).toOptional();
|
||||
if (result.isPresent()) {
|
||||
e = result.get().method();
|
||||
tags = result.get().specTrees();
|
||||
}
|
||||
}
|
||||
return specTagOutput(e, tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the output for one or more {@code @spec} tags.
|
||||
*
|
||||
* @param holder the element that owns the doc comment
|
||||
* @param specTags the array of @spec tags.
|
||||
*
|
||||
* @return the output
|
||||
*/
|
||||
public Content specTagOutput(Element holder, List<? extends SpecTree> specTags) {
|
||||
if (specTags.isEmpty()) {
|
||||
return Text.EMPTY;
|
||||
}
|
||||
|
||||
var links = specTags.stream()
|
||||
.map(st -> specTagToContent(holder, st)).toList();
|
||||
|
||||
var specList = tagletWriter.tagList(links);
|
||||
return new ContentBuilder(
|
||||
HtmlTree.DT(contents.externalSpecifications),
|
||||
HtmlTree.DD(specList));
|
||||
}
|
||||
|
||||
private record Documentation(List<? extends SpecTree> specTrees, ExecutableElement method) { }
|
||||
|
||||
private static Optional<Documentation> extract(Utils utils, ExecutableElement method) {
|
||||
List<? extends SpecTree> tags = utils.getSpecTrees(method);
|
||||
return tags.isEmpty() ? Optional.empty() : Optional.of(new Documentation(tags, method));
|
||||
}
|
||||
|
||||
private Content specTagToContent(Element holder, SpecTree specTree) {
|
||||
var htmlWriter = tagletWriter.htmlWriter;
|
||||
String specTreeURL = specTree.getURL().getBody();
|
||||
List<? extends DocTree> specTreeLabel = specTree.getTitle();
|
||||
Content label = htmlWriter.commentTagsToContent(holder, specTreeLabel, tagletWriter.context.isFirstSentence);
|
||||
return getExternalSpecContent(holder, specTree, specTreeURL,
|
||||
textOf(specTreeLabel).replaceAll("\\s+", " "), label);
|
||||
}
|
||||
|
||||
private String textOf(List<? extends DocTree> trees) {
|
||||
return trees.stream()
|
||||
.filter(dt -> dt instanceof TextTree)
|
||||
.map(dt -> ((TextTree) dt).getBody().trim())
|
||||
.collect(Collectors.joining(" "));
|
||||
}
|
||||
|
||||
Content getExternalSpecContent(Element holder,
|
||||
DocTree docTree,
|
||||
String url,
|
||||
String searchText,
|
||||
Content title) {
|
||||
URI specURI;
|
||||
try {
|
||||
// Use the canonical title of the spec if one is available
|
||||
specURI = new URI(url);
|
||||
} catch (URISyntaxException e) {
|
||||
CommentHelper ch = utils.getCommentHelper(holder);
|
||||
DocTreePath dtp = ch.getDocTreePath(docTree);
|
||||
tagletWriter.htmlWriter.messages.error(dtp, "doclet.Invalid_URL", e.getMessage());
|
||||
specURI = null;
|
||||
}
|
||||
|
||||
Content titleWithAnchor = tagletWriter.createAnchorAndSearchIndex(holder,
|
||||
searchText,
|
||||
title,
|
||||
resources.getText("doclet.External_Specification"),
|
||||
docTree);
|
||||
|
||||
if (specURI == null) {
|
||||
return titleWithAnchor;
|
||||
} else {
|
||||
var htmlWriter = tagletWriter.htmlWriter;
|
||||
return HtmlTree.A(htmlWriter.resolveExternalSpecURI(specURI), titleWithAnchor);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,14 +23,17 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets;
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import jdk.javadoc.doclet.Taglet.Location;
|
||||
import com.sun.source.doctree.SummaryTree;
|
||||
|
||||
import jdk.javadoc.doclet.Taglet.Location;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
|
||||
/**
|
||||
@ -38,13 +41,13 @@ import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
*/
|
||||
public class SummaryTaglet extends BaseTaglet {
|
||||
|
||||
public SummaryTaglet() {
|
||||
super(DocTree.Kind.SUMMARY, true, EnumSet.allOf(Location.class));
|
||||
SummaryTaglet(HtmlConfiguration config) {
|
||||
super(config, DocTree.Kind.SUMMARY, true, EnumSet.allOf(Location.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getInlineTagOutput(Element holder, DocTree tag, TagletWriter writer) {
|
||||
return writer.commentTagsToOutput(holder, tag, ((SummaryTree)tag).getSummary(),
|
||||
writer.isFirstSentence);
|
||||
public Content getInlineTagOutput(Element holder, DocTree tag, TagletWriter tagletWriter) {
|
||||
return tagletWriter.commentTagsToOutput(holder, tag, ((SummaryTree)tag).getSummary(),
|
||||
tagletWriter.context.isFirstSentence);
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,27 +23,46 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets;
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import com.sun.source.doctree.SystemPropertyTree;
|
||||
import jdk.javadoc.doclet.Taglet.Location;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
import java.util.EnumSet;
|
||||
import jdk.javadoc.doclet.Taglet;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
|
||||
/**
|
||||
* A taglet that represents the {@code @systemProperty} tag.
|
||||
*/
|
||||
public class SystemPropertyTaglet extends BaseTaglet {
|
||||
|
||||
SystemPropertyTaglet() {
|
||||
super(DocTree.Kind.SYSTEM_PROPERTY, true, EnumSet.allOf(Location.class));
|
||||
SystemPropertyTaglet(HtmlConfiguration config) {
|
||||
super(config, DocTree.Kind.SYSTEM_PROPERTY, true, EnumSet.allOf(Taglet.Location.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getInlineTagOutput(Element element, DocTree tag, TagletWriter writer) {
|
||||
return writer.systemPropertyTagOutput(element, (SystemPropertyTree) tag);
|
||||
public Content getInlineTagOutput(Element element, DocTree tag, TagletWriter tagletWriter) {
|
||||
this.tagletWriter = tagletWriter;
|
||||
return systemPropertyTagOutput(element, (SystemPropertyTree) tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the output for a {@code {@systemProperty...}} tag.
|
||||
*
|
||||
* @param element the element that owns the doc comment
|
||||
* @param tag the system property tag
|
||||
*
|
||||
* @return the output
|
||||
*/
|
||||
private Content systemPropertyTagOutput(Element element, SystemPropertyTree tag) {
|
||||
String tagText = tag.getPropertyName().toString();
|
||||
return HtmlTree.CODE(tagletWriter.createAnchorAndSearchIndex(element, tagText,
|
||||
resources.getText("doclet.System_Property"), tag));
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,17 +23,25 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets;
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets;
|
||||
|
||||
import java.util.Set;
|
||||
import javax.lang.model.element.Element;
|
||||
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import jdk.javadoc.doclet.Taglet.Location;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
|
||||
/**
|
||||
* This is the taglet interface used internally within the doclet.
|
||||
*
|
||||
* The public {@link jdk.javadoc.doclet.Taglet} interface only supports
|
||||
* output to strings. This interface supports structured output,
|
||||
* to {@link Content} objects, such as {@link HtmlTree}.
|
||||
*
|
||||
* User-provided taglets are supported using the {@link UserTaglet}
|
||||
* wrapper class.
|
||||
*/
|
||||
public interface Taglet {
|
||||
/**
|
||||
@ -49,7 +57,9 @@ public interface Taglet {
|
||||
* @return {@code true} if this {@code Taglet} can be used in field documentation
|
||||
* and {@code false} otherwise
|
||||
*/
|
||||
boolean inField();
|
||||
default boolean inField() {
|
||||
return getAllowedLocations().contains(Location.FIELD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this {@code Taglet} can be used in constructor documentation.
|
||||
@ -57,7 +67,9 @@ public interface Taglet {
|
||||
* @return {@code true} if this {@code Taglet} can be used in constructor documentation
|
||||
* and {@code false} otherwise
|
||||
*/
|
||||
boolean inConstructor();
|
||||
default boolean inConstructor() {
|
||||
return getAllowedLocations().contains(Location.CONSTRUCTOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this {@code Taglet} can be used in method documentation.
|
||||
@ -65,7 +77,9 @@ public interface Taglet {
|
||||
* @return {@code true} if this {@code Taglet} can be used in method documentation
|
||||
* and {@code false} otherwise
|
||||
*/
|
||||
boolean inMethod();
|
||||
default boolean inMethod() {
|
||||
return getAllowedLocations().contains(Location.METHOD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this {@code Taglet} can be used in overview documentation.
|
||||
@ -73,7 +87,9 @@ public interface Taglet {
|
||||
* @return {@code true} if this {@code Taglet} can be used in overview documentation
|
||||
* and {@code false} otherwise
|
||||
*/
|
||||
boolean inOverview();
|
||||
default boolean inOverview() {
|
||||
return getAllowedLocations().contains(Location.OVERVIEW);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this {@code Taglet} can be used in module documentation.
|
||||
@ -81,7 +97,9 @@ public interface Taglet {
|
||||
* @return {@code true} if this {@code Taglet} can be used in module documentation
|
||||
* and {@code false} otherwise
|
||||
*/
|
||||
boolean inModule();
|
||||
default boolean inModule() {
|
||||
return getAllowedLocations().contains(Location.MODULE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this {@code Taglet} can be used in package documentation.
|
||||
@ -89,7 +107,9 @@ public interface Taglet {
|
||||
* @return {@code true} if this {@code Taglet} can be used in package documentation
|
||||
* and {@code false} otherwise
|
||||
*/
|
||||
boolean inPackage();
|
||||
default boolean inPackage() {
|
||||
return getAllowedLocations().contains(Location.PACKAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this {@code Taglet} can be used in type documentation (classes or interfaces).
|
||||
@ -97,7 +117,9 @@ public interface Taglet {
|
||||
* @return {@code true} if this {@code Taglet} can be used in type documentation
|
||||
* and {@code false} otherwise
|
||||
*/
|
||||
boolean inType();
|
||||
default boolean inType() {
|
||||
return getAllowedLocations().contains(Location.TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this {@code Taglet} represents an inline tag.
|
||||
@ -130,12 +152,12 @@ public interface Taglet {
|
||||
*
|
||||
* @param owner the element for the enclosing doc comment
|
||||
* @param tag the tag
|
||||
* @param writer the taglet-writer used in this doclet
|
||||
* @param tagletWriter the taglet-writer used in this doclet
|
||||
*
|
||||
* @return the output for this tag
|
||||
* @throws UnsupportedTagletOperationException if the method is not supported by the taglet
|
||||
*/
|
||||
Content getInlineTagOutput(Element owner, DocTree tag, TagletWriter writer) throws
|
||||
Content getInlineTagOutput(Element owner, DocTree tag, TagletWriter tagletWriter) throws
|
||||
UnsupportedTagletOperationException;
|
||||
|
||||
/**
|
||||
@ -143,12 +165,12 @@ public interface Taglet {
|
||||
* all instances of block tags handled by this taglet.
|
||||
*
|
||||
* @param owner the element for the enclosing doc comment
|
||||
* @param writer the taglet-writer used in this doclet
|
||||
* @param tagletWriter the taglet-writer used in this doclet
|
||||
*
|
||||
* @return the output for this tag
|
||||
* @throws UnsupportedTagletOperationException if the method is not supported by the taglet
|
||||
*/
|
||||
Content getAllBlockTagOutput(Element owner, TagletWriter writer) throws
|
||||
Content getAllBlockTagOutput(Element owner, TagletWriter tagletWriter) throws
|
||||
UnsupportedTagletOperationException;
|
||||
|
||||
class UnsupportedTagletOperationException extends UnsupportedOperationException {
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,7 +23,7 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets;
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@ -55,8 +55,8 @@ import com.sun.source.doctree.DocTree;
|
||||
import jdk.javadoc.doclet.Doclet;
|
||||
import jdk.javadoc.doclet.DocletEnvironment;
|
||||
import jdk.javadoc.doclet.Taglet.Location;
|
||||
import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
|
||||
import jdk.javadoc.internal.doclets.toolkit.BaseOptions;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlOptions;
|
||||
import jdk.javadoc.internal.doclets.toolkit.DocletElement;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Messages;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Resources;
|
||||
@ -66,8 +66,6 @@ import jdk.javadoc.internal.doclets.toolkit.util.Utils;
|
||||
import static com.sun.source.doctree.DocTree.Kind.AUTHOR;
|
||||
import static com.sun.source.doctree.DocTree.Kind.EXCEPTION;
|
||||
import static com.sun.source.doctree.DocTree.Kind.HIDDEN;
|
||||
import static com.sun.source.doctree.DocTree.Kind.LINK;
|
||||
import static com.sun.source.doctree.DocTree.Kind.LINK_PLAIN;
|
||||
import static com.sun.source.doctree.DocTree.Kind.PARAM;
|
||||
import static com.sun.source.doctree.DocTree.Kind.PROVIDES;
|
||||
import static com.sun.source.doctree.DocTree.Kind.SEE;
|
||||
@ -78,10 +76,11 @@ import static com.sun.source.doctree.DocTree.Kind.SINCE;
|
||||
import static com.sun.source.doctree.DocTree.Kind.THROWS;
|
||||
import static com.sun.source.doctree.DocTree.Kind.USES;
|
||||
import static com.sun.source.doctree.DocTree.Kind.VERSION;
|
||||
|
||||
import static javax.tools.DocumentationTool.Location.TAGLET_PATH;
|
||||
|
||||
/**
|
||||
* Manages the {@code Taglet}s used by doclets.
|
||||
* Manages the {@code Taglet}s used by the standard doclet.
|
||||
*/
|
||||
public class TagletManager {
|
||||
|
||||
@ -173,32 +172,32 @@ public class TagletManager {
|
||||
|
||||
private final String tagletPath;
|
||||
|
||||
private final BaseConfiguration configuration;
|
||||
private final HtmlConfiguration config;
|
||||
|
||||
/**
|
||||
* Constructs a new {@code TagletManager}.
|
||||
*
|
||||
* @param configuration the configuration for this taglet manager
|
||||
* @param config the configuration for this taglet manager
|
||||
*/
|
||||
public TagletManager(BaseConfiguration configuration) {
|
||||
public TagletManager(HtmlConfiguration config) {
|
||||
overriddenStandardTags = new HashSet<>();
|
||||
potentiallyConflictingTags = new HashSet<>();
|
||||
standardTags = new HashSet<>();
|
||||
standardTagsLowercase = new HashSet<>();
|
||||
unseenCustomTags = new HashSet<>();
|
||||
allTaglets = new LinkedHashMap<>();
|
||||
this.configuration = configuration;
|
||||
BaseOptions options = configuration.getOptions();
|
||||
this.config = config;
|
||||
HtmlOptions options = config.getOptions();
|
||||
this.nosince = options.noSince();
|
||||
this.showversion = options.showVersion();
|
||||
this.showauthor = options.showAuthor();
|
||||
this.javafx = options.javafx();
|
||||
this.docEnv = configuration.docEnv;
|
||||
this.doclet = configuration.doclet;
|
||||
this.messages = configuration.getMessages();
|
||||
this.resources = configuration.getDocResources();
|
||||
this.docEnv = config.docEnv;
|
||||
this.doclet = config.doclet;
|
||||
this.messages = config.getMessages();
|
||||
this.resources = config.getDocResources();
|
||||
this.showTaglets = options.showTaglets();
|
||||
this.utils = configuration.utils;
|
||||
this.utils = config.utils;
|
||||
this.tagletPath = options.tagletPath();
|
||||
initStandardTaglets();
|
||||
}
|
||||
@ -239,7 +238,7 @@ public class TagletManager {
|
||||
*/
|
||||
public void addCustomTag(String classname, JavaFileManager fileManager) {
|
||||
ClassLoader tagClassLoader = fileManager.getClassLoader(TAGLET_PATH);
|
||||
if (configuration.workArounds.accessInternalAPI()) {
|
||||
if (config.workArounds.accessInternalAPI()) {
|
||||
Module thisModule = getClass().getModule();
|
||||
Module tagletLoaderUnnamedModule = tagClassLoader.getUnnamedModule();
|
||||
List<String> pkgs = List.of(
|
||||
@ -309,7 +308,7 @@ public class TagletManager {
|
||||
// remove + put in both branches below move the tag to the back of the map's ordering
|
||||
Taglet tag = allTaglets.remove(tagName);
|
||||
if (tag == null || header != null) {
|
||||
allTaglets.put(tagName, new SimpleTaglet(tagName, header, locations));
|
||||
allTaglets.put(tagName, new SimpleTaglet(config, tagName, header, locations));
|
||||
if (Utils.toLowerCase(locations).indexOf('x') == -1) {
|
||||
checkTagName(tagName);
|
||||
}
|
||||
@ -371,7 +370,7 @@ public class TagletManager {
|
||||
final Taglet taglet = allTaglets.get(name);
|
||||
// Check and verify tag usage
|
||||
if (taglet != null) {
|
||||
if (taglet instanceof SimpleTaglet st && !st.enabled) {
|
||||
if (taglet instanceof SimpleTaglet st && !st.isEnabled()) {
|
||||
// taglet has been disabled
|
||||
return;
|
||||
}
|
||||
@ -591,69 +590,69 @@ public class TagletManager {
|
||||
initJavaFXTaglets();
|
||||
}
|
||||
|
||||
addStandardTaglet(new ParamTaglet());
|
||||
addStandardTaglet(new ReturnTaglet());
|
||||
addStandardTaglet(new ThrowsTaglet(configuration), EXCEPTION);
|
||||
addStandardTaglet(new ParamTaglet(config));
|
||||
addStandardTaglet(new ReturnTaglet(config));
|
||||
addStandardTaglet(new ThrowsTaglet(config), EXCEPTION);
|
||||
addStandardTaglet(
|
||||
new SimpleTaglet(SINCE, resources.getText("doclet.Since"),
|
||||
new SimpleTaglet(config, SINCE, resources.getText("doclet.Since"),
|
||||
EnumSet.allOf(Location.class), !nosince));
|
||||
addStandardTaglet(
|
||||
new SimpleTaglet(VERSION, resources.getText("doclet.Version"),
|
||||
new SimpleTaglet(config, VERSION, resources.getText("doclet.Version"),
|
||||
EnumSet.of(Location.OVERVIEW, Location.MODULE, Location.PACKAGE, Location.TYPE), showversion));
|
||||
addStandardTaglet(
|
||||
new SimpleTaglet(AUTHOR, resources.getText("doclet.Author"),
|
||||
new SimpleTaglet(config, AUTHOR, resources.getText("doclet.Author"),
|
||||
EnumSet.of(Location.OVERVIEW, Location.MODULE, Location.PACKAGE, Location.TYPE), showauthor));
|
||||
addStandardTaglet(
|
||||
new SimpleTaglet(SERIAL_DATA, resources.getText("doclet.SerialData"),
|
||||
new SimpleTaglet(config, SERIAL_DATA, resources.getText("doclet.SerialData"),
|
||||
EnumSet.noneOf(Location.class)));
|
||||
addStandardTaglet(
|
||||
new SimpleTaglet(HIDDEN, null,
|
||||
new SimpleTaglet(config, HIDDEN, null,
|
||||
EnumSet.of(Location.TYPE, Location.METHOD, Location.FIELD)));
|
||||
|
||||
// This appears to be a default custom (non-standard) taglet
|
||||
Taglet factoryTaglet = new SimpleTaglet("factory", resources.getText("doclet.Factory"),
|
||||
Taglet factoryTaglet = new SimpleTaglet(config, "factory", resources.getText("doclet.Factory"),
|
||||
EnumSet.of(Location.METHOD));
|
||||
allTaglets.put(factoryTaglet.getName(), factoryTaglet);
|
||||
|
||||
addStandardTaglet(new SeeTaglet());
|
||||
addStandardTaglet(new SpecTaglet());
|
||||
addStandardTaglet(new SeeTaglet(config));
|
||||
addStandardTaglet(new SpecTaglet(config));
|
||||
|
||||
// Standard inline tags
|
||||
addStandardTaglet(new DocRootTaglet());
|
||||
addStandardTaglet(new InheritDocTaglet());
|
||||
addStandardTaglet(new ValueTaglet());
|
||||
addStandardTaglet(new LiteralTaglet());
|
||||
addStandardTaglet(new CodeTaglet());
|
||||
addStandardTaglet(new SnippetTaglet());
|
||||
addStandardTaglet(new IndexTaglet());
|
||||
addStandardTaglet(new SummaryTaglet());
|
||||
addStandardTaglet(new SystemPropertyTaglet());
|
||||
addStandardTaglet(new DocRootTaglet(config));
|
||||
addStandardTaglet(new InheritDocTaglet(config));
|
||||
addStandardTaglet(new ValueTaglet(config));
|
||||
addStandardTaglet(new LinkTaglet(config, DocTree.Kind.LINK));
|
||||
addStandardTaglet(new LinkTaglet(config, DocTree.Kind.LINK_PLAIN));
|
||||
addStandardTaglet(new LiteralTaglet(config, DocTree.Kind.CODE));
|
||||
addStandardTaglet(new LiteralTaglet(config, DocTree.Kind.LITERAL));
|
||||
addStandardTaglet(new SnippetTaglet(config));
|
||||
addStandardTaglet(new IndexTaglet(config));
|
||||
addStandardTaglet(new SummaryTaglet(config));
|
||||
addStandardTaglet(new SystemPropertyTaglet(config));
|
||||
|
||||
// Keep track of the names of standard tags for error checking purposes.
|
||||
// The following are not handled above.
|
||||
addStandardTaglet(new DeprecatedTaglet());
|
||||
addStandardTaglet(new BaseTaglet(LINK, true, EnumSet.allOf(Location.class)));
|
||||
addStandardTaglet(new BaseTaglet(LINK_PLAIN, true, EnumSet.allOf(Location.class)));
|
||||
addStandardTaglet(new BaseTaglet(USES, false, EnumSet.of(Location.MODULE)));
|
||||
addStandardTaglet(new BaseTaglet(PROVIDES, false, EnumSet.of(Location.MODULE)));
|
||||
addStandardTaglet(new DeprecatedTaglet(config));
|
||||
addStandardTaglet(new BaseTaglet(config, USES, false, EnumSet.of(jdk.javadoc.doclet.Taglet.Location.MODULE)));
|
||||
addStandardTaglet(new BaseTaglet(config, PROVIDES, false, EnumSet.of(jdk.javadoc.doclet.Taglet.Location.MODULE)));
|
||||
addStandardTaglet(
|
||||
new SimpleTaglet(SERIAL, null,
|
||||
EnumSet.of(Location.PACKAGE, Location.TYPE, Location.FIELD)));
|
||||
new SimpleTaglet(config, SERIAL, null,
|
||||
EnumSet.of(jdk.javadoc.doclet.Taglet.Location.PACKAGE, jdk.javadoc.doclet.Taglet.Location.TYPE, jdk.javadoc.doclet.Taglet.Location.FIELD)));
|
||||
addStandardTaglet(
|
||||
new SimpleTaglet(SERIAL_FIELD, null, EnumSet.of(Location.FIELD)));
|
||||
new SimpleTaglet(config, SERIAL_FIELD, null, EnumSet.of(jdk.javadoc.doclet.Taglet.Location.FIELD)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize JavaFX-related tags.
|
||||
*/
|
||||
private void initJavaFXTaglets() {
|
||||
addStandardTaglet(new SimpleTaglet("propertyDescription",
|
||||
addStandardTaglet(new SimpleTaglet(config, "propertyDescription",
|
||||
resources.getText("doclet.PropertyDescription"),
|
||||
EnumSet.of(Location.METHOD, Location.FIELD)));
|
||||
addStandardTaglet(new SimpleTaglet("defaultValue", resources.getText("doclet.DefaultValue"),
|
||||
EnumSet.of(Location.METHOD, Location.FIELD)));
|
||||
addStandardTaglet(new SimpleTaglet("treatAsPrivate", null,
|
||||
EnumSet.of(Location.TYPE, Location.METHOD, Location.FIELD)));
|
||||
EnumSet.of(jdk.javadoc.doclet.Taglet.Location.METHOD, jdk.javadoc.doclet.Taglet.Location.FIELD)));
|
||||
addStandardTaglet(new SimpleTaglet(config, "defaultValue", resources.getText("doclet.DefaultValue"),
|
||||
EnumSet.of(jdk.javadoc.doclet.Taglet.Location.METHOD, jdk.javadoc.doclet.Taglet.Location.FIELD)));
|
||||
addStandardTaglet(new SimpleTaglet(config, "treatAsPrivate", null,
|
||||
EnumSet.of(jdk.javadoc.doclet.Taglet.Location.TYPE, jdk.javadoc.doclet.Taglet.Location.METHOD, jdk.javadoc.doclet.Taglet.Location.FIELD)));
|
||||
}
|
||||
|
||||
private void addStandardTaglet(Taglet taglet) {
|
||||
@ -711,6 +710,14 @@ public class TagletManager {
|
||||
}
|
||||
}
|
||||
|
||||
public Taglet getTaglet(DocTree.Kind kind) {
|
||||
return switch (kind) {
|
||||
case DEPRECATED, LINK, LINK_PLAIN, PARAM, RETURN, THROWS -> getTaglet(kind.tagName);
|
||||
default ->
|
||||
throw new IllegalArgumentException(kind.toString());
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* The output of this method is the basis for a table at the end of the
|
||||
* doc comment specification, so any changes in the output may indicate
|
||||
@ -732,7 +739,7 @@ public class TagletManager {
|
||||
+ format(t.inMethod(), "method") + " "
|
||||
+ format(t.inField(), "field") + " "
|
||||
+ format(t.isInlineTag(), "inline")+ " "
|
||||
+ format((t instanceof SimpleTaglet st) && !st.enabled, "disabled"));
|
||||
+ format((t instanceof SimpleTaglet st) && !st.isEnabled(), "disabled"));
|
||||
});
|
||||
}
|
||||
|
@ -0,0 +1,474 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.ModuleElement;
|
||||
import javax.lang.model.element.PackageElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.util.SimpleElementVisitor14;
|
||||
|
||||
import com.sun.source.doctree.DocTree;
|
||||
|
||||
import com.sun.source.doctree.InlineTagTree;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlDocletWriter;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlIds;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlOptions;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlId;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.Text;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
import jdk.javadoc.internal.doclets.formats.html.taglets.Taglet.UnsupportedTagletOperationException;
|
||||
import jdk.javadoc.internal.doclets.toolkit.DocletElement;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Resources;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocLink;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.IndexItem;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.Utils;
|
||||
|
||||
/**
|
||||
* Context and utility methods for taglet classes.
|
||||
*/
|
||||
public class TagletWriter {
|
||||
|
||||
/**
|
||||
* A class that provides the information about the enclosing context for
|
||||
* a series of {@code DocTree} nodes.
|
||||
* This context may be used to determine the content that should be generated from the tree nodes.
|
||||
*/
|
||||
public static class Context {
|
||||
/**
|
||||
* Whether the trees are appearing in a context of just the first sentence,
|
||||
* such as in the summary table of the enclosing element.
|
||||
*/
|
||||
public final boolean isFirstSentence;
|
||||
/**
|
||||
* Whether the trees are appearing in the "summary" section of the
|
||||
* page for a declaration.
|
||||
*/
|
||||
public final boolean inSummary;
|
||||
/**
|
||||
* The set of enclosing kinds of tags.
|
||||
*/
|
||||
public final Set<DocTree.Kind> inTags;
|
||||
|
||||
/**
|
||||
* Creates an outermost context, with no enclosing tags.
|
||||
*
|
||||
* @param isFirstSentence {@code true} if the trees are appearing in a context of just the
|
||||
* first sentence and {@code false} otherwise
|
||||
* @param inSummary {@code true} if the trees are appearing in the "summary" section
|
||||
* of the page for a declaration and {@code false} otherwise
|
||||
*/
|
||||
public Context(boolean isFirstSentence, boolean inSummary) {
|
||||
this(isFirstSentence, inSummary, EnumSet.noneOf(DocTree.Kind.class));
|
||||
}
|
||||
|
||||
private Context(boolean isFirstSentence, boolean inSummary, Set<DocTree.Kind> inTags) {
|
||||
this.isFirstSentence = isFirstSentence;
|
||||
this.inSummary = inSummary;
|
||||
this.inTags = inTags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code Context} that includes an extra tag kind in the set of enclosing
|
||||
* kinds of tags.
|
||||
*
|
||||
* @param tree the enclosing tree
|
||||
*
|
||||
* @return the new {@code Context}
|
||||
*/
|
||||
public Context within(DocTree tree) {
|
||||
var newInTags = EnumSet.copyOf(inTags);
|
||||
newInTags.add(tree.getKind());
|
||||
return new Context(isFirstSentence, inSummary, newInTags);
|
||||
}
|
||||
}
|
||||
|
||||
public final HtmlDocletWriter htmlWriter;
|
||||
public final HtmlConfiguration configuration;
|
||||
public final HtmlOptions options;
|
||||
public final Utils utils;
|
||||
public final Resources resources;
|
||||
|
||||
/**
|
||||
* The context in which to generate the output for a series of {@code DocTree} nodes.
|
||||
*/
|
||||
public final Context context;
|
||||
/**
|
||||
* Creates a taglet writer.
|
||||
*
|
||||
* @param htmlWriter the {@code HtmlDocletWriter} for the page
|
||||
* @param isFirstSentence {@code true} if this taglet writer is being used for a
|
||||
* "first sentence" summary
|
||||
*/
|
||||
public TagletWriter(HtmlDocletWriter htmlWriter, boolean isFirstSentence) {
|
||||
this(htmlWriter, isFirstSentence, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a taglet writer.
|
||||
*
|
||||
* @param htmlWriter the {@code HtmlDocletWriter} for the page
|
||||
* @param isFirstSentence {@code true} if this taglet writer is being used for a
|
||||
* "first sentence" summary, and {@code false} otherwise
|
||||
* @param inSummary {@code true} if this taglet writer is being used for the content
|
||||
* of a {@code {@summary ...}} tag, and {@code false} otherwise
|
||||
*/
|
||||
public TagletWriter(HtmlDocletWriter htmlWriter, boolean isFirstSentence, boolean inSummary) {
|
||||
this(htmlWriter, new Context(isFirstSentence, inSummary));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a taglet writer.
|
||||
*
|
||||
* @param htmlWriter the {@code HtmlDocletWriter} for the page
|
||||
* @param context the enclosing context for any tags
|
||||
*/
|
||||
public TagletWriter(HtmlDocletWriter htmlWriter, Context context) {
|
||||
this.htmlWriter = Objects.requireNonNull(htmlWriter);
|
||||
this.context = Objects.requireNonNull(context);
|
||||
configuration = htmlWriter.configuration;
|
||||
options = configuration.getOptions();
|
||||
utils = configuration.utils;
|
||||
resources = configuration.getDocResources();
|
||||
}
|
||||
|
||||
public Context getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of an output object.
|
||||
*
|
||||
* @return an instance of an output object
|
||||
*/
|
||||
public Content getOutputInstance() {
|
||||
return new ContentBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the output for an invalid tag. The returned content uses special styling to
|
||||
* highlight the problem. Depending on the presence of the {@code detail} string the method
|
||||
* returns a plain text span or an expandable component.
|
||||
*
|
||||
* @param summary the single-line summary message
|
||||
* @param detail the optional detail message which may contain preformatted text
|
||||
* @return the output
|
||||
*/
|
||||
public Content invalidTagOutput(String summary, Optional<String> detail) {
|
||||
return htmlWriter.invalidTagOutput(summary,
|
||||
detail.isEmpty() || detail.get().isEmpty()
|
||||
? Optional.empty()
|
||||
: Optional.of(Text.of(Text.normalizeNewlines(detail.get()))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the main type element of the current page or null for pages that don't have one.
|
||||
*
|
||||
* @return the type element of the current page or null.
|
||||
*/
|
||||
public TypeElement getCurrentPageElement() {
|
||||
return htmlWriter.getCurrentPageElement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content generated from the block tags for a given element.
|
||||
* The content is generated according to the order of the list of taglets.
|
||||
* The result is a possibly-empty list of the output generated by each
|
||||
* of the given taglets for all of the tags they individually support.
|
||||
*
|
||||
* @param tagletManager the manager that manages the taglets
|
||||
* @param element the element that we are to write tags for
|
||||
* @param taglets the taglets for the tags to write
|
||||
*
|
||||
* @return the content
|
||||
*/
|
||||
public Content getBlockTagOutput(TagletManager tagletManager,
|
||||
Element element,
|
||||
List<Taglet> taglets) {
|
||||
for (Taglet t : taglets) {
|
||||
if (!t.isBlockTag()) {
|
||||
throw new IllegalArgumentException(t.getName());
|
||||
}
|
||||
}
|
||||
|
||||
Content output = getOutputInstance();
|
||||
tagletManager.checkTags(element, utils.getBlockTags(element));
|
||||
tagletManager.checkTags(element, utils.getFullBody(element));
|
||||
for (Taglet taglet : taglets) {
|
||||
if (utils.isTypeElement(element) && taglet instanceof ParamTaglet) {
|
||||
// The type parameters and state components are documented in a special
|
||||
// section away from the tag info, so skip here.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (element.getKind() == ElementKind.MODULE && taglet instanceof BaseTaglet t) {
|
||||
switch (t.getTagKind()) {
|
||||
// @uses and @provides are handled separately, so skip here.
|
||||
// See ModuleWriterImpl.computeModulesData
|
||||
case USES:
|
||||
case PROVIDES:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (taglet instanceof DeprecatedTaglet) {
|
||||
//Deprecated information is documented "inline", not in tag info
|
||||
//section.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (taglet instanceof SimpleTaglet st && !st.isEnabled()) {
|
||||
// taglet has been disabled
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
Content tagletOutput = taglet.getAllBlockTagOutput(element, this);
|
||||
if (tagletOutput != null) {
|
||||
tagletManager.seenTag(taglet.getName());
|
||||
output.add(tagletOutput);
|
||||
}
|
||||
} catch (UnsupportedTagletOperationException e) {
|
||||
// malformed taglet:
|
||||
// claims to support block tags (see Taglet.isBlockTag) but does not provide the
|
||||
// appropriate method, Taglet.getAllBlockTagOutput.
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content generated from an inline tag in the doc comment for a given element,
|
||||
* or {@code null} if the tag is not supported or does not return any output.
|
||||
*
|
||||
* @param holder the element associated with the doc comment
|
||||
* @param inlineTag the inline tag to be documented
|
||||
*
|
||||
* @return the content, or {@code null}
|
||||
*/
|
||||
public Content getInlineTagOutput(Element holder,
|
||||
InlineTagTree inlineTag) {
|
||||
var tagletManager = configuration.tagletManager;
|
||||
Map<String, Taglet> inlineTags = tagletManager.getInlineTaglets();
|
||||
CommentHelper ch = configuration.utils.getCommentHelper(holder);
|
||||
final String inlineTagName = ch.getTagName(inlineTag);
|
||||
Taglet t = inlineTags.get(inlineTagName);
|
||||
if (t == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
Content tagletOutput = t.getInlineTagOutput(holder, inlineTag, this);
|
||||
tagletManager.seenTag(t.getName());
|
||||
return tagletOutput;
|
||||
} catch (UnsupportedTagletOperationException e) {
|
||||
// malformed taglet:
|
||||
// claims to support inline tags (see Taglet.isInlineTag) but does not provide the
|
||||
// appropriate method, Taglet.getInlineTagOutput.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts inline tags and text to content, expanding the
|
||||
* inline tags along the way. Called wherever text can contain
|
||||
* an inline tag, such as in comments or in free-form text arguments
|
||||
* to block tags.
|
||||
*
|
||||
* @param holderTree the tree that holds the documentation
|
||||
* @param trees list of {@code DocTree} nodes containing text and inline tags (often alternating)
|
||||
* present in the text of interest for this doc
|
||||
*
|
||||
* @return the generated content
|
||||
*/
|
||||
public Content commentTagsToOutput(DocTree holderTree, List<? extends DocTree> trees) {
|
||||
return commentTagsToOutput(null, holderTree, trees, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts inline tags and text to content, expanding the
|
||||
* inline tags along the way. Called wherever text can contain
|
||||
* an inline tag, such as in comments or in free-form text arguments
|
||||
* to block tags.
|
||||
*
|
||||
* @param element The element that owns the documentation
|
||||
* @param trees list of {@code DocTree} nodes containing text and inline tags (often alternating)
|
||||
* present in the text of interest for this doc
|
||||
*
|
||||
* @return the generated content
|
||||
*/
|
||||
public Content commentTagsToOutput(Element element, List<? extends DocTree> trees) {
|
||||
return commentTagsToOutput(element, null, trees, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts inline tags and text to content, expanding the
|
||||
* inline tags along the way. Called wherever text can contain
|
||||
* an inline tag, such as in comments or in free-form text arguments
|
||||
* to non-inline tags.
|
||||
*
|
||||
* @param element the element where comment resides
|
||||
* @param holder the tag that holds the documentation
|
||||
* @param trees array of text tags and inline tags (often alternating)
|
||||
* present in the text of interest for this doc
|
||||
* @param isFirstSentence true if this is the first sentence
|
||||
*
|
||||
* @return the generated content
|
||||
*/
|
||||
public Content commentTagsToOutput(Element element,
|
||||
DocTree holder,
|
||||
List<? extends DocTree> trees,
|
||||
boolean isFirstSentence)
|
||||
{
|
||||
return htmlWriter.commentTagsToContent(element,
|
||||
trees, holder == null ? context : context.within(holder));
|
||||
}
|
||||
|
||||
public Content createAnchorAndSearchIndex(Element element, String tagText, String desc, DocTree tree) {
|
||||
return createAnchorAndSearchIndex(element, tagText, Text.of(tagText), desc, tree);
|
||||
}
|
||||
|
||||
@SuppressWarnings("preview")
|
||||
Content createAnchorAndSearchIndex(Element element, String tagText, Content tagContent, String desc, DocTree tree) {
|
||||
Content result;
|
||||
if (context.isFirstSentence && context.inSummary || context.inTags.contains(DocTree.Kind.INDEX)) {
|
||||
result = tagContent;
|
||||
} else {
|
||||
HtmlId id = HtmlIds.forText(tagText, htmlWriter.indexAnchorTable);
|
||||
result = HtmlTree.SPAN(id, HtmlStyle.searchTagResult, tagContent);
|
||||
if (options.createIndex() && !tagText.isEmpty()) {
|
||||
String holder = getHolderName(element);
|
||||
IndexItem item = IndexItem.of(element, tree, tagText, holder, desc,
|
||||
new DocLink(htmlWriter.path, id.name()));
|
||||
configuration.mainIndex.add(item);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public String getHolderName(Element element) {
|
||||
return new SimpleElementVisitor14<String, Void>() {
|
||||
|
||||
@Override
|
||||
public String visitModule(ModuleElement e, Void p) {
|
||||
return resources.getText("doclet.module")
|
||||
+ " " + utils.getFullyQualifiedName(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitPackage(PackageElement e, Void p) {
|
||||
return resources.getText("doclet.package")
|
||||
+ " " + utils.getFullyQualifiedName(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitType(TypeElement e, Void p) {
|
||||
return utils.getTypeElementKindName(e, true)
|
||||
+ " " + utils.getFullyQualifiedName(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitExecutable(ExecutableElement e, Void p) {
|
||||
return utils.getFullyQualifiedName(utils.getEnclosingTypeElement(e))
|
||||
+ "." + utils.getSimpleName(e)
|
||||
+ utils.flatSignature(e, htmlWriter.getCurrentPageElement());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitVariable(VariableElement e, Void p) {
|
||||
return utils.getFullyQualifiedName(utils.getEnclosingTypeElement(e))
|
||||
+ "." + utils.getSimpleName(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitUnknown(Element e, Void p) {
|
||||
if (e instanceof DocletElement de) {
|
||||
return switch (de.getSubKind()) {
|
||||
case OVERVIEW -> resources.getText("doclet.Overview");
|
||||
case DOCFILE -> getHolderName(de);
|
||||
};
|
||||
} else {
|
||||
return super.visitUnknown(e, p);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String defaultAction(Element e, Void p) {
|
||||
return utils.getFullyQualifiedName(e);
|
||||
}
|
||||
}.visit(element);
|
||||
}
|
||||
|
||||
private String getHolderName(DocletElement de) {
|
||||
PackageElement pe = de.getPackageElement();
|
||||
if (pe.isUnnamed()) {
|
||||
// if package is unnamed use enclosing module only if it is named
|
||||
Element ee = pe.getEnclosingElement();
|
||||
if (ee instanceof ModuleElement && !((ModuleElement)ee).isUnnamed()) {
|
||||
return resources.getText("doclet.module") + " " + utils.getFullyQualifiedName(ee);
|
||||
}
|
||||
return pe.toString(); // "Unnamed package" or similar
|
||||
}
|
||||
return resources.getText("doclet.package") + " " + utils.getFullyQualifiedName(pe);
|
||||
}
|
||||
|
||||
Content tagList(List<Content> items) {
|
||||
// Use a different style if any list item is longer than 30 chars or contains commas.
|
||||
boolean hasLongLabels = items.stream().anyMatch(this::isLongOrHasComma);
|
||||
var list = HtmlTree.UL(hasLongLabels ? HtmlStyle.tagListLong : HtmlStyle.tagList);
|
||||
items.stream()
|
||||
.filter(Predicate.not(Content::isEmpty))
|
||||
.forEach(item -> list.add(HtmlTree.LI(item)));
|
||||
return list;
|
||||
}
|
||||
|
||||
// Threshold for length of list item for switching from inline to block layout.
|
||||
private static final int TAG_LIST_ITEM_MAX_INLINE_LENGTH = 30;
|
||||
|
||||
private boolean isLongOrHasComma(Content c) {
|
||||
String s = c.toString()
|
||||
.replaceAll("<.*?>", "") // ignore HTML
|
||||
.replaceAll("&#?[A-Za-z0-9]+;", " ") // entities count as a single character
|
||||
.replaceAll("\\R", "\n"); // normalize newlines
|
||||
return s.length() > TAG_LIST_ITEM_MAX_INLINE_LENGTH || s.contains(",");
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets;
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
@ -53,13 +53,14 @@ import com.sun.source.doctree.DocTree;
|
||||
import com.sun.source.doctree.InheritDocTree;
|
||||
import com.sun.source.doctree.ThrowsTree;
|
||||
|
||||
import com.sun.source.util.DocTreePath;
|
||||
import jdk.javadoc.doclet.Taglet.Location;
|
||||
import jdk.javadoc.doclet.Taglet;
|
||||
import jdk.javadoc.internal.doclets.formats.html.Contents;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlLinkInfo;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
|
||||
import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocFinder;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocFinder.Result;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.Utils;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable;
|
||||
|
||||
@ -156,19 +157,19 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
* mA as a member of the supertype of C that names A.
|
||||
*/
|
||||
|
||||
public ThrowsTaglet(BaseConfiguration configuration) {
|
||||
private final HtmlConfiguration config;
|
||||
private final Contents contents;
|
||||
|
||||
ThrowsTaglet(HtmlConfiguration config) {
|
||||
// of all language elements only constructors and methods can declare
|
||||
// thrown exceptions and, hence, document them
|
||||
super(DocTree.Kind.THROWS, false, EnumSet.of(Location.CONSTRUCTOR, Location.METHOD));
|
||||
this.configuration = configuration;
|
||||
this.utils = this.configuration.utils;
|
||||
super(config, DocTree.Kind.THROWS, false, EnumSet.of(Taglet.Location.CONSTRUCTOR, Taglet.Location.METHOD));
|
||||
this.config = config;
|
||||
contents = config.contents;
|
||||
}
|
||||
|
||||
private final BaseConfiguration configuration;
|
||||
private final Utils utils;
|
||||
|
||||
@Override
|
||||
public Output inherit(Element dst, Element src, DocTree tag, boolean isFirstSentence, BaseConfiguration configuration) {
|
||||
public Output inherit(Element dst, Element src, DocTree tag, boolean isFirstSentence) {
|
||||
// This method shouldn't be called because {@inheritDoc} tags inside
|
||||
// exception tags aren't dealt with individually. {@inheritDoc} tags
|
||||
// inside exception tags are collectively dealt with in
|
||||
@ -177,13 +178,13 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getAllBlockTagOutput(Element holder, TagletWriter writer) {
|
||||
public Content getAllBlockTagOutput(Element holder, TagletWriter tagletWriter) {
|
||||
this.tagletWriter = tagletWriter;
|
||||
try {
|
||||
return getAllBlockTagOutput0(holder, writer);
|
||||
return getAllBlockTagOutput0(holder);
|
||||
} catch (Failure f) {
|
||||
// note that `f.holder()` is not necessarily the same as `holder`
|
||||
var ch = utils.getCommentHelper(f.holder());
|
||||
var messages = configuration.getMessages();
|
||||
if (f instanceof Failure.ExceptionTypeNotFound e) {
|
||||
var path = ch.getDocTreePath(e.tag().getExceptionName());
|
||||
messages.warning(path, "doclet.throws.reference_not_found");
|
||||
@ -210,14 +211,13 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
// since {@inheritDoc} in @throws is processed by ThrowsTaglet (this taglet) rather than
|
||||
// InheritDocTaglet, we have to duplicate some of the behavior of the latter taglet
|
||||
String signature = utils.getSimpleName(holder)
|
||||
+ utils.flatSignature((ExecutableElement) holder, writer.getCurrentPageElement());
|
||||
configuration.getMessages().warning(holder, "doclet.noInheritedDoc", signature);
|
||||
+ utils.flatSignature((ExecutableElement) holder, tagletWriter.getCurrentPageElement());
|
||||
messages.warning(holder, "doclet.noInheritedDoc", signature);
|
||||
}
|
||||
return writer.getOutputInstance(); // TODO: consider invalid rather than empty output
|
||||
return tagletWriter.getOutputInstance(); // TODO: consider invalid rather than empty output
|
||||
}
|
||||
|
||||
private Content getAllBlockTagOutput0(Element holder,
|
||||
TagletWriter writer)
|
||||
private Content getAllBlockTagOutput0(Element holder)
|
||||
throws Failure.ExceptionTypeNotFound,
|
||||
Failure.NotExceptionType,
|
||||
Failure.Invalid,
|
||||
@ -235,20 +235,20 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
}
|
||||
var executable = (ExecutableElement) holder;
|
||||
ExecutableType instantiatedType = utils.asInstantiatedMethodType(
|
||||
writer.getCurrentPageElement(), executable);
|
||||
tagletWriter.getCurrentPageElement(), executable);
|
||||
List<? extends TypeMirror> substitutedExceptionTypes = instantiatedType.getThrownTypes();
|
||||
List<? extends TypeMirror> originalExceptionTypes = executable.getThrownTypes();
|
||||
Map<TypeMirror, TypeMirror> typeSubstitutions = getSubstitutedThrownTypes(
|
||||
utils.typeUtils,
|
||||
originalExceptionTypes,
|
||||
substitutedExceptionTypes);
|
||||
var exceptionSection = new ExceptionSectionBuilder(writer);
|
||||
var exceptionSection = new ExceptionSectionBuilder(tagletWriter, this);
|
||||
// Step 1: Document exception tags
|
||||
Set<TypeMirror> alreadyDocumentedExceptions = new HashSet<>();
|
||||
List<ThrowsTree> exceptionTags = utils.getThrowsTrees(executable);
|
||||
for (ThrowsTree t : exceptionTags) {
|
||||
Element exceptionElement = getExceptionType(t, executable);
|
||||
outputAnExceptionTagDeeply(exceptionSection, exceptionElement, t, executable, alreadyDocumentedExceptions, typeSubstitutions, writer);
|
||||
outputAnExceptionTagDeeply(exceptionSection, exceptionElement, t, executable, alreadyDocumentedExceptions, typeSubstitutions);
|
||||
}
|
||||
// Step 2: Document exception types from the `throws` clause (of a method)
|
||||
//
|
||||
@ -277,7 +277,7 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
continue;
|
||||
}
|
||||
for (Map.Entry<ThrowsTree, ExecutableElement> e : r.entrySet()) {
|
||||
outputAnExceptionTagDeeply(exceptionSection, exceptionElement, e.getKey(), e.getValue(), alreadyDocumentedExceptions, typeSubstitutions, writer);
|
||||
outputAnExceptionTagDeeply(exceptionSection, exceptionElement, e.getKey(), e.getValue(), alreadyDocumentedExceptions, typeSubstitutions);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -299,13 +299,41 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
return exceptionSection.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the header for the {@code @throws} tag.
|
||||
*
|
||||
* @return the header for the throws tag
|
||||
*/
|
||||
private Content getThrowsHeader() {
|
||||
return HtmlTree.DT(contents.throws_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the output for a default {@code @throws} tag.
|
||||
*
|
||||
* @param throwsType the type that is thrown
|
||||
* @param content the optional content to add as a description
|
||||
*
|
||||
* @return the output
|
||||
*/
|
||||
private Content throwsTagOutput(TypeMirror throwsType, Optional<Content> content) {
|
||||
var htmlWriter = tagletWriter.htmlWriter;
|
||||
var linkInfo = new HtmlLinkInfo(config, HtmlLinkInfo.Kind.PLAIN, throwsType);
|
||||
var link = htmlWriter.getLink(linkInfo);
|
||||
var concat = new ContentBuilder(HtmlTree.CODE(link));
|
||||
if (content.isPresent()) {
|
||||
concat.add(" - ");
|
||||
concat.add(content.get());
|
||||
}
|
||||
return HtmlTree.DD(concat);
|
||||
}
|
||||
|
||||
private void outputAnExceptionTagDeeply(ExceptionSectionBuilder exceptionSection,
|
||||
Element originalExceptionElement,
|
||||
ThrowsTree tag,
|
||||
ExecutableElement holder,
|
||||
Set<TypeMirror> alreadyDocumentedExceptions,
|
||||
Map<TypeMirror, TypeMirror> typeSubstitutions,
|
||||
TagletWriter writer)
|
||||
Map<TypeMirror, TypeMirror> typeSubstitutions)
|
||||
throws Failure.ExceptionTypeNotFound,
|
||||
Failure.NotExceptionType,
|
||||
Failure.Invalid,
|
||||
@ -314,7 +342,7 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
Failure.NoOverrideFound,
|
||||
DocFinder.NoOverriddenMethodFound
|
||||
{
|
||||
outputAnExceptionTagDeeply(exceptionSection, originalExceptionElement, tag, holder, true, alreadyDocumentedExceptions, typeSubstitutions, writer);
|
||||
outputAnExceptionTagDeeply(exceptionSection, originalExceptionElement, tag, holder, true, alreadyDocumentedExceptions, typeSubstitutions);
|
||||
}
|
||||
|
||||
private void outputAnExceptionTagDeeply(ExceptionSectionBuilder exceptionSection,
|
||||
@ -323,8 +351,7 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
ExecutableElement holder,
|
||||
boolean beginNewEntry,
|
||||
Set<TypeMirror> alreadyDocumentedExceptions,
|
||||
Map<TypeMirror, TypeMirror> typeSubstitutions,
|
||||
TagletWriter writer)
|
||||
Map<TypeMirror, TypeMirror> typeSubstitutions)
|
||||
throws Failure.ExceptionTypeNotFound,
|
||||
Failure.NotExceptionType,
|
||||
Failure.Invalid,
|
||||
@ -355,7 +382,7 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
exceptionSection.beginEntry(exceptionType);
|
||||
}
|
||||
// append to the current entry
|
||||
exceptionSection.continueEntry(writer.commentTagsToOutput(holder, description));
|
||||
exceptionSection.continueEntry(tagletWriter.commentTagsToOutput(holder, description));
|
||||
if (beginNewEntry) { // if added a new entry, end it
|
||||
exceptionSection.endEntry();
|
||||
}
|
||||
@ -373,7 +400,7 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
if (i > 0) {
|
||||
// if there's anything preceding {@inheritDoc}, assume an entry has been added before
|
||||
assert exceptionSection.debugEntryBegun();
|
||||
Content beforeInheritDoc = writer.commentTagsToOutput(holder, description.subList(0, i));
|
||||
Content beforeInheritDoc = tagletWriter.commentTagsToOutput(holder, description.subList(0, i));
|
||||
exceptionSection.continueEntry(beforeInheritDoc);
|
||||
}
|
||||
|
||||
@ -386,7 +413,7 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
if (supertype == null) {
|
||||
throw new Failure.NoOverrideFound(tag, holder, inheritDoc);
|
||||
}
|
||||
VisibleMemberTable visibleMemberTable = configuration.getVisibleMemberTable(supertype);
|
||||
VisibleMemberTable visibleMemberTable = config.getVisibleMemberTable(supertype);
|
||||
List<Element> methods = visibleMemberTable.getAllVisibleMembers(VisibleMemberTable.Kind.METHODS);
|
||||
for (Element e : methods) {
|
||||
ExecutableElement m = (ExecutableElement) e;
|
||||
@ -422,11 +449,11 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
throw new Failure.Invalid(tag, holder);
|
||||
}
|
||||
for (Map.Entry<ThrowsTree, ExecutableElement> e : tags.entrySet()) {
|
||||
outputAnExceptionTagDeeply(exceptionSection, originalExceptionElement, e.getKey(), e.getValue(), addNewEntryRecursively, alreadyDocumentedExceptions, typeSubstitutions, writer);
|
||||
outputAnExceptionTagDeeply(exceptionSection, originalExceptionElement, e.getKey(), e.getValue(), addNewEntryRecursively, alreadyDocumentedExceptions, typeSubstitutions);
|
||||
}
|
||||
// this might be an empty list, which is fine
|
||||
if (!loneInheritDoc) {
|
||||
Content afterInheritDoc = writer.commentTagsToOutput(holder, description.subList(i + 1, description.size()));
|
||||
Content afterInheritDoc = tagletWriter.commentTagsToOutput(holder, description.subList(i + 1, description.size()));
|
||||
exceptionSection.continueEntry(afterInheritDoc);
|
||||
}
|
||||
if (add) {
|
||||
@ -604,7 +631,7 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
return toResult(exceptionType, method, tags);
|
||||
};
|
||||
}
|
||||
Result<Map<ThrowsTree, ExecutableElement>> result;
|
||||
DocFinder.Result<Map<ThrowsTree, ExecutableElement>> result;
|
||||
try {
|
||||
if (src.isPresent()) {
|
||||
result = utils.docFinder().search(src.get(), criterion);
|
||||
@ -623,20 +650,20 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
} catch (Failure f) {
|
||||
throw newAssertionError(f);
|
||||
}
|
||||
if (result instanceof Result.Conclude<Map<ThrowsTree, ExecutableElement>> c) {
|
||||
if (result instanceof DocFinder.Result.Conclude<Map<ThrowsTree, ExecutableElement>> c) {
|
||||
return c.value();
|
||||
}
|
||||
return Map.of(); // an empty map is effectively ordered
|
||||
}
|
||||
|
||||
private static Result<Map<ThrowsTree, ExecutableElement>> toResult(Element target,
|
||||
ExecutableElement holder,
|
||||
List<ThrowsTree> tags) {
|
||||
private static DocFinder.Result<Map<ThrowsTree, ExecutableElement>> toResult(Element target,
|
||||
ExecutableElement holder,
|
||||
List<ThrowsTree> tags) {
|
||||
if (!tags.isEmpty()) {
|
||||
// if there are tags for the target exception type, conclude search successfully
|
||||
return Result.CONCLUDE(toExceptionTags(holder, tags));
|
||||
return DocFinder.Result.CONCLUDE(toExceptionTags(holder, tags));
|
||||
}
|
||||
return Result.CONTINUE();
|
||||
return DocFinder.Result.CONTINUE();
|
||||
// TODO: reintroduce this back for JDK-8295800:
|
||||
// if (holder.getThrownTypes().contains(target.asType())) {
|
||||
// // if there are no tags for the target exception type, BUT that type is
|
||||
@ -694,14 +721,16 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
private static class ExceptionSectionBuilder {
|
||||
|
||||
private final TagletWriter writer;
|
||||
private final ThrowsTaglet taglet;
|
||||
private final Content result;
|
||||
private ContentBuilder current;
|
||||
private Content current;
|
||||
private boolean began;
|
||||
private boolean headerAdded;
|
||||
private TypeMirror exceptionType;
|
||||
|
||||
ExceptionSectionBuilder(TagletWriter writer) {
|
||||
ExceptionSectionBuilder(TagletWriter writer, ThrowsTaglet taglet) {
|
||||
this.writer = writer;
|
||||
this.taglet = taglet;
|
||||
this.result = writer.getOutputInstance();
|
||||
}
|
||||
|
||||
@ -710,7 +739,7 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
began = true;
|
||||
current = new ContentBuilder();
|
||||
current = writer.getOutputInstance();
|
||||
this.exceptionType = exceptionType;
|
||||
}
|
||||
|
||||
@ -728,9 +757,10 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
began = false;
|
||||
if (!headerAdded) {
|
||||
headerAdded = true;
|
||||
result.add(writer.getThrowsHeader());
|
||||
result.add(taglet.getThrowsHeader());
|
||||
}
|
||||
result.add(writer.throwsTagOutput(exceptionType, current.isEmpty() ? Optional.empty() : Optional.of(current)));
|
||||
result.add(taglet.throwsTagOutput(exceptionType,
|
||||
current.isEmpty() ? Optional.empty() : Optional.of(current)));
|
||||
current = null;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,7 +23,7 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets;
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@ -31,11 +31,9 @@ import java.util.Set;
|
||||
import javax.lang.model.element.Element;
|
||||
|
||||
import com.sun.source.doctree.DocTree;
|
||||
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.RawHtml;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.Utils;
|
||||
|
||||
import static jdk.javadoc.doclet.Taglet.Location.*;
|
||||
|
||||
/**
|
||||
* A taglet wrapper, allows the public taglet {@link jdk.javadoc.doclet.Taglet}
|
||||
@ -45,7 +43,7 @@ public final class UserTaglet implements Taglet {
|
||||
|
||||
private final jdk.javadoc.doclet.Taglet userTaglet;
|
||||
|
||||
public UserTaglet(jdk.javadoc.doclet.Taglet t) {
|
||||
UserTaglet(jdk.javadoc.doclet.Taglet t) {
|
||||
userTaglet = t;
|
||||
}
|
||||
|
||||
@ -54,41 +52,6 @@ public final class UserTaglet implements Taglet {
|
||||
return userTaglet.getAllowedLocations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inField() {
|
||||
return userTaglet.getAllowedLocations().contains(FIELD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inConstructor() {
|
||||
return userTaglet.getAllowedLocations().contains(CONSTRUCTOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inMethod() {
|
||||
return userTaglet.getAllowedLocations().contains(METHOD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inOverview() {
|
||||
return userTaglet.getAllowedLocations().contains(OVERVIEW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inModule() {
|
||||
return userTaglet.getAllowedLocations().contains(MODULE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inPackage() {
|
||||
return userTaglet.getAllowedLocations().contains(PACKAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inType() {
|
||||
return userTaglet.getAllowedLocations().contains(TYPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInlineTag() {
|
||||
return userTaglet.isInlineTag();
|
||||
@ -105,17 +68,17 @@ public final class UserTaglet implements Taglet {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getInlineTagOutput(Element element, DocTree tag, TagletWriter writer) {
|
||||
Content output = writer.getOutputInstance();
|
||||
public Content getInlineTagOutput(Element element, DocTree tag, TagletWriter tagletWriter) {
|
||||
Content output = tagletWriter.getOutputInstance();
|
||||
output.add(RawHtml.of(userTaglet.toString(List.of(tag), element)));
|
||||
return output;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getAllBlockTagOutput(Element holder, TagletWriter writer) {
|
||||
Content output = writer.getOutputInstance();
|
||||
Utils utils = writer.configuration().utils;
|
||||
List<? extends DocTree> tags = utils.getBlockTags(holder, this);
|
||||
public Content getAllBlockTagOutput(Element holder, TagletWriter tagletWriter) {
|
||||
Content output = tagletWriter.getOutputInstance();
|
||||
var utils = tagletWriter.utils;
|
||||
List<? extends DocTree> tags = utils.getBlockTags(holder, getName());
|
||||
if (!tags.isEmpty()) {
|
||||
String tagString = userTaglet.toString(tags, holder);
|
||||
if (tagString != null) {
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,23 +23,26 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets;
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.IllegalFormatException;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import com.sun.source.doctree.TextTree;
|
||||
import com.sun.source.doctree.ValueTree;
|
||||
import jdk.javadoc.doclet.Taglet.Location;
|
||||
|
||||
import jdk.javadoc.doclet.Taglet;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlLinkInfo;
|
||||
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.Messages;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.Utils;
|
||||
|
||||
/**
|
||||
* An inline taglet representing the value tag. This tag should only be used with
|
||||
@ -52,11 +55,8 @@ import jdk.javadoc.internal.doclets.toolkit.util.Utils;
|
||||
*/
|
||||
public class ValueTaglet extends BaseTaglet {
|
||||
|
||||
/**
|
||||
* Construct a new ValueTaglet.
|
||||
*/
|
||||
public ValueTaglet() {
|
||||
super(DocTree.Kind.VALUE, true, EnumSet.allOf(Location.class));
|
||||
ValueTaglet(HtmlConfiguration config) {
|
||||
super(config, DocTree.Kind.VALUE, true, EnumSet.allOf(Taglet.Location.class));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -83,11 +83,9 @@ public class ValueTaglet extends BaseTaglet {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getInlineTagOutput(Element holder, DocTree tag, TagletWriter writer) {
|
||||
BaseConfiguration configuration = writer.configuration();
|
||||
Utils utils = configuration.utils;
|
||||
Messages messages = configuration.getMessages();
|
||||
VariableElement field = getVariableElement(holder, configuration, tag);
|
||||
public Content getInlineTagOutput(Element holder, DocTree tag, TagletWriter tagletWriter) {
|
||||
this.tagletWriter = tagletWriter;
|
||||
VariableElement field = getVariableElement(holder, config, tag);
|
||||
if (field == null) {
|
||||
if (tag.toString().isEmpty()) {
|
||||
//Invalid use of @value
|
||||
@ -107,25 +105,40 @@ public class ValueTaglet extends BaseTaglet {
|
||||
f = f.substring(1, f.length() - 1);
|
||||
}
|
||||
try {
|
||||
text = String.format(configuration.getLocale(), f, field.getConstantValue());
|
||||
text = String.format(config.getLocale(), f, field.getConstantValue());
|
||||
} catch (IllegalFormatException e) {
|
||||
messages.error(holder,
|
||||
"doclet.value_tag_invalid_format", format);
|
||||
return writer.invalidTagOutput(
|
||||
return tagletWriter.invalidTagOutput(
|
||||
messages.getResources().getText("doclet.value_tag_invalid_format", format),
|
||||
Optional.empty());
|
||||
}
|
||||
} else {
|
||||
text = utils.constantValueExpression(field);
|
||||
}
|
||||
return writer.valueTagOutput(field,
|
||||
text,
|
||||
!field.equals(holder));
|
||||
return valueTagOutput(field, text, !field.equals(holder));
|
||||
} else {
|
||||
//Referenced field is not a constant.
|
||||
messages.warning(holder,
|
||||
"doclet.value_tag_invalid_constant", utils.getSimpleName(field));
|
||||
"doclet.value_tag_invalid_constant", utils.getSimpleName(field));
|
||||
}
|
||||
return writer.getOutputInstance();
|
||||
return tagletWriter.getOutputInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the output for a {@code {@value}} tag.
|
||||
*
|
||||
* @param field the constant field that holds the value tag
|
||||
* @param constantVal the constant value to document
|
||||
* @param includeLink true if we should link the constant text to the
|
||||
* constant field itself
|
||||
*
|
||||
* @return the output
|
||||
*/
|
||||
private Content valueTagOutput(VariableElement field, String constantVal, boolean includeLink) {
|
||||
var htmlWriter = tagletWriter.htmlWriter;
|
||||
return includeLink
|
||||
? htmlWriter.getDocLink(HtmlLinkInfo.Kind.LINK_TYPE_PARAMS_AND_BOUNDS, field, constantVal)
|
||||
: Text.of(constantVal);
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,29 +23,21 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import javax.lang.model.element.Element;
|
||||
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import com.sun.source.doctree.IndexTree;
|
||||
import jdk.javadoc.doclet.Taglet.Location;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
|
||||
/**
|
||||
* An inline taglet used to index a word or a phrase.
|
||||
* The enclosed text is interpreted as not containing HTML markup or
|
||||
* nested javadoc tags.
|
||||
* Classes used to build the output for documentation comment tags.
|
||||
*
|
||||
* Tags are either inline tags, meaning they can be used within a
|
||||
* sentence or phrase, or are block tags, meaning that they provide
|
||||
* additional details that follow the main description in a comment.
|
||||
* Taglets model that distinction.
|
||||
*
|
||||
* Inline tags are always processed individually, within the surrounding
|
||||
* context. In general, inline tags always generate some (non-empty) output,
|
||||
* even if the output is some form indicating an error. It is almost never
|
||||
* correct to not generate any output to place between the parts of the
|
||||
* comment that come before and after the tag in the underlying comment.
|
||||
*
|
||||
* Conversely, block tags of any given kind are always processed as a
|
||||
* group, even if they do not appear contiguously in the underlying comment.
|
||||
*/
|
||||
public class IndexTaglet extends BaseTaglet {
|
||||
|
||||
IndexTaglet() {
|
||||
super(DocTree.Kind.INDEX, true, EnumSet.allOf(Location.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getInlineTagOutput(Element element, DocTree tag, TagletWriter writer) {
|
||||
return writer.indexTagOutput(element, (IndexTree) tag);
|
||||
}
|
||||
}
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets;
|
@ -23,7 +23,7 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets.snippet;
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets.snippet;
|
||||
|
||||
/**
|
||||
* An action described by markup. Such an action is typically an opaque compound
|
@ -23,7 +23,7 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets.snippet;
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets.snippet;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
@ -23,7 +23,7 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets.snippet;
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets.snippet;
|
||||
|
||||
import java.util.Objects;
|
||||
|
@ -23,7 +23,7 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets.snippet;
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets.snippet;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
@ -23,7 +23,7 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets.snippet;
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets.snippet;
|
||||
|
||||
/**
|
||||
* An action that associates text with a name.
|
@ -23,7 +23,7 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets.snippet;
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets.snippet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
@ -23,7 +23,7 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets.snippet;
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets.snippet;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
@ -23,7 +23,7 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets.snippet;
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets.snippet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
@ -38,8 +38,8 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
import jdk.javadoc.internal.doclets.formats.html.taglets.SnippetTaglet;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Resources;
|
||||
import jdk.javadoc.internal.doclets.toolkit.taglets.SnippetTaglet;
|
||||
|
||||
/*
|
||||
* Semantics of a EOL comment; plus
|
@ -23,7 +23,7 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets.snippet;
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets.snippet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Set;
|
@ -23,7 +23,7 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets.snippet;
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets.snippet;
|
||||
|
||||
/**
|
||||
* A style of a snippet text character.
|
@ -23,7 +23,7 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets.snippet;
|
||||
package jdk.javadoc.internal.doclets.formats.html.taglets.snippet;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,7 +25,6 @@
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.Function;
|
||||
@ -36,8 +35,6 @@ import javax.lang.model.element.TypeElement;
|
||||
|
||||
import jdk.javadoc.doclet.Doclet;
|
||||
import jdk.javadoc.doclet.DocletEnvironment;
|
||||
import jdk.javadoc.doclet.StandardDoclet;
|
||||
import jdk.javadoc.internal.doclets.formats.html.HtmlDoclet;
|
||||
import jdk.javadoc.internal.doclets.toolkit.builders.AbstractBuilder;
|
||||
import jdk.javadoc.internal.doclets.toolkit.builders.BuilderFactory;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.ClassTree;
|
||||
@ -68,25 +65,6 @@ public abstract class AbstractDoclet implements Doclet {
|
||||
*/
|
||||
protected Utils utils;
|
||||
|
||||
/**
|
||||
* The only doclet that may use this toolkit is {@value}
|
||||
*/
|
||||
private static final String TOOLKIT_DOCLET_NAME =
|
||||
jdk.javadoc.internal.doclets.formats.html.HtmlDoclet.class.getName();
|
||||
|
||||
/**
|
||||
* Verify that the only doclet that is using this toolkit is
|
||||
* #TOOLKIT_DOCLET_NAME.
|
||||
*/
|
||||
private boolean isValidDoclet() {
|
||||
if (!getClass().getName().equals(TOOLKIT_DOCLET_NAME)) {
|
||||
messages.error("doclet.Toolkit_Usage_Violation",
|
||||
TOOLKIT_DOCLET_NAME);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The method that starts the execution of the doclet.
|
||||
*
|
||||
@ -101,13 +79,9 @@ public abstract class AbstractDoclet implements Doclet {
|
||||
messages = configuration.getMessages();
|
||||
BaseOptions options = configuration.getOptions();
|
||||
|
||||
if (!isValidDoclet()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
try {
|
||||
startGeneration();
|
||||
generateFiles();
|
||||
return true;
|
||||
} catch (UncheckedDocletException e) {
|
||||
throw (DocletException) e.getCause();
|
||||
@ -151,7 +125,7 @@ public abstract class AbstractDoclet implements Doclet {
|
||||
}
|
||||
|
||||
private void reportInternalError(Throwable t) {
|
||||
if (getClass().equals(StandardDoclet.class) || getClass().equals(HtmlDoclet.class)) {
|
||||
if (getClass().getModule() == AbstractDoclet.class.getModule()) {
|
||||
System.err.println(configuration.getDocResources().getText("doclet.internal.report.bug"));
|
||||
}
|
||||
dumpStack(true, t);
|
||||
@ -188,7 +162,7 @@ public abstract class AbstractDoclet implements Doclet {
|
||||
*
|
||||
* @throws DocletException if there is a problem while generating the documentation
|
||||
*/
|
||||
private void startGeneration() throws DocletException {
|
||||
protected void generateFiles() throws DocletException {
|
||||
|
||||
// Modules with no documented classes may be specified on the
|
||||
// command line to specify a service provider, allow these.
|
||||
@ -211,7 +185,6 @@ public abstract class AbstractDoclet implements Doclet {
|
||||
generateModuleFiles();
|
||||
|
||||
generateOtherFiles(classTree);
|
||||
configuration.tagletManager.printReport();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,12 +26,7 @@
|
||||
package jdk.javadoc.internal.doclets.toolkit;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@ -53,10 +48,8 @@ import javax.lang.model.element.PackageElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.util.Elements;
|
||||
import javax.lang.model.util.SimpleElementVisitor14;
|
||||
import javax.tools.DocumentationTool;
|
||||
import javax.tools.JavaFileManager;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.StandardJavaFileManager;
|
||||
|
||||
import com.sun.source.tree.CompilationUnitTree;
|
||||
import com.sun.source.util.TreePath;
|
||||
@ -68,7 +61,6 @@ import jdk.javadoc.doclet.Reporter;
|
||||
import jdk.javadoc.doclet.StandardDoclet;
|
||||
import jdk.javadoc.doclet.Taglet;
|
||||
import jdk.javadoc.internal.doclets.toolkit.builders.BuilderFactory;
|
||||
import jdk.javadoc.internal.doclets.toolkit.taglets.TagletManager;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.Comparators;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocFile;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocFileFactory;
|
||||
@ -100,11 +92,6 @@ public abstract class BaseConfiguration {
|
||||
*/
|
||||
protected BuilderFactory builderFactory;
|
||||
|
||||
/**
|
||||
* The taglet manager.
|
||||
*/
|
||||
public TagletManager tagletManager;
|
||||
|
||||
/**
|
||||
* The meta tag keywords instance.
|
||||
*/
|
||||
@ -374,28 +361,6 @@ public abstract class BaseConfiguration {
|
||||
}
|
||||
typeElementCatalog = new TypeElementCatalog(includedTypeElements, this);
|
||||
|
||||
String snippetPath = options.snippetPath();
|
||||
if (snippetPath != null) {
|
||||
Messages messages = getMessages();
|
||||
JavaFileManager fm = getFileManager();
|
||||
if (fm instanceof StandardJavaFileManager) {
|
||||
try {
|
||||
List<Path> sp = Arrays.stream(snippetPath.split(File.pathSeparator))
|
||||
.map(Path::of)
|
||||
.toList();
|
||||
StandardJavaFileManager sfm = (StandardJavaFileManager) fm;
|
||||
sfm.setLocationFromPaths(DocumentationTool.Location.SNIPPET_PATH, sp);
|
||||
} catch (IOException | InvalidPathException e) {
|
||||
throw new SimpleDocletException(messages.getResources().getText(
|
||||
"doclet.error_setting_snippet_path", snippetPath, e), e);
|
||||
}
|
||||
} else {
|
||||
throw new SimpleDocletException(messages.getResources().getText(
|
||||
"doclet.cannot_use_snippet_path", snippetPath));
|
||||
}
|
||||
}
|
||||
|
||||
initTagletManager(options.customTagStrs());
|
||||
options.groupPairs().forEach(grp -> {
|
||||
if (showModules) {
|
||||
group.checkModuleGroups(grp.first, grp.second);
|
||||
@ -451,100 +416,6 @@ public abstract class BaseConfiguration {
|
||||
DocFileFactory.getFactory(this).setDestDir(destDirName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the taglet manager. The strings to initialize the simple custom tags should
|
||||
* be in the following format: "[tag name]:[location str]:[heading]".
|
||||
*
|
||||
* @param customTagStrs the set two dimensional arrays of strings. These arrays contain
|
||||
* either -tag or -taglet arguments.
|
||||
*/
|
||||
private void initTagletManager(Set<List<String>> customTagStrs) {
|
||||
tagletManager = tagletManager != null ? tagletManager : new TagletManager(this);
|
||||
JavaFileManager fileManager = getFileManager();
|
||||
Messages messages = getMessages();
|
||||
try {
|
||||
tagletManager.initTagletPath(fileManager);
|
||||
tagletManager.loadTaglets(fileManager);
|
||||
|
||||
for (List<String> args : customTagStrs) {
|
||||
if (args.get(0).equals("-taglet")) {
|
||||
tagletManager.addCustomTag(args.get(1), fileManager);
|
||||
continue;
|
||||
}
|
||||
/* Since there are few constraints on the characters in a tag name,
|
||||
* and real world examples with ':' in the tag name, we cannot simply use
|
||||
* String.split(regex); instead, we tokenize the string, allowing
|
||||
* special characters to be escaped with '\'. */
|
||||
List<String> tokens = tokenize(args.get(1), 3);
|
||||
switch (tokens.size()) {
|
||||
case 1 -> {
|
||||
String tagName = args.get(1);
|
||||
if (tagletManager.isKnownCustomTag(tagName)) {
|
||||
//reorder a standard tag
|
||||
tagletManager.addNewSimpleCustomTag(tagName, null, "");
|
||||
} else {
|
||||
//Create a simple tag with the heading that has the same name as the tag.
|
||||
StringBuilder heading = new StringBuilder(tagName + ":");
|
||||
heading.setCharAt(0, Character.toUpperCase(tagName.charAt(0)));
|
||||
tagletManager.addNewSimpleCustomTag(tagName, heading.toString(), "a");
|
||||
}
|
||||
}
|
||||
|
||||
case 2 ->
|
||||
//Add simple taglet without heading, probably to excluding it in the output.
|
||||
tagletManager.addNewSimpleCustomTag(tokens.get(0), tokens.get(1), "");
|
||||
|
||||
case 3 ->
|
||||
tagletManager.addNewSimpleCustomTag(tokens.get(0), tokens.get(2), tokens.get(1));
|
||||
|
||||
default ->
|
||||
messages.error("doclet.Error_invalid_custom_tag_argument", args.get(1));
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
messages.error("doclet.taglet_could_not_set_location", e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a string, return an array of tokens, separated by ':'.
|
||||
* The separator character can be escaped with the '\' character.
|
||||
* The '\' character may also be escaped with the '\' character.
|
||||
*
|
||||
* @param s the string to tokenize
|
||||
* @param maxTokens the maximum number of tokens returned. If the
|
||||
* max is reached, the remaining part of s is appended
|
||||
* to the end of the last token.
|
||||
* @return an array of tokens
|
||||
*/
|
||||
private List<String> tokenize(String s, int maxTokens) {
|
||||
List<String> tokens = new ArrayList<>();
|
||||
StringBuilder token = new StringBuilder();
|
||||
boolean prevIsEscapeChar = false;
|
||||
for (int i = 0; i < s.length(); i += Character.charCount(i)) {
|
||||
int currentChar = s.codePointAt(i);
|
||||
if (prevIsEscapeChar) {
|
||||
// Case 1: escaped character
|
||||
token.appendCodePoint(currentChar);
|
||||
prevIsEscapeChar = false;
|
||||
} else if (currentChar == ':' && tokens.size() < maxTokens - 1) {
|
||||
// Case 2: separator
|
||||
tokens.add(token.toString());
|
||||
token = new StringBuilder();
|
||||
} else if (currentChar == '\\') {
|
||||
// Case 3: escape character
|
||||
prevIsEscapeChar = true;
|
||||
} else {
|
||||
// Case 4: regular character
|
||||
token.appendCodePoint(currentChar);
|
||||
}
|
||||
}
|
||||
if (token.length() > 0) {
|
||||
tokens.add(token.toString());
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the given doc-file subdirectory should be excluded and
|
||||
* false otherwise.
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -32,29 +32,20 @@ import java.io.OutputStreamWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.time.temporal.TemporalUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import jdk.javadoc.doclet.Doclet;
|
||||
import jdk.javadoc.doclet.Reporter;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.Utils;
|
||||
|
||||
import static javax.tools.Diagnostic.Kind.ERROR;
|
||||
@ -87,11 +78,6 @@ public abstract class BaseOptions {
|
||||
*/
|
||||
private boolean copyDocfileSubdirs = false;
|
||||
|
||||
/**
|
||||
* Arguments for command-line option {@code -tag} and {@code -taglet}.
|
||||
*/
|
||||
private final LinkedHashSet<List<String>> customTagStrs = new LinkedHashSet<>();
|
||||
|
||||
/**
|
||||
* Argument for command-line option {@code --date}.
|
||||
* {@code null} if option not given.
|
||||
@ -265,12 +251,6 @@ public abstract class BaseOptions {
|
||||
*/
|
||||
private boolean showAuthor = false;
|
||||
|
||||
/**
|
||||
* Argument for command-line option {@code --show-taglets}.
|
||||
* Show taglets (internal debug switch)
|
||||
*/
|
||||
private boolean showTaglets = false;
|
||||
|
||||
/**
|
||||
* Argument for command-line option {@code -version}.
|
||||
* Generate version specific information for the all the classes
|
||||
@ -313,18 +293,6 @@ public abstract class BaseOptions {
|
||||
*/
|
||||
private boolean summarizeOverriddenMethods = false;
|
||||
|
||||
/**
|
||||
* Argument for command-line option {@code -tagletpath}.
|
||||
* The path to Taglets
|
||||
*/
|
||||
private String tagletPath = null;
|
||||
|
||||
/**
|
||||
* Argument for command-line option {@code --snippet-path}.
|
||||
* The path for external snippets.
|
||||
*/
|
||||
private String snippetPath = null;
|
||||
|
||||
//</editor-fold>
|
||||
|
||||
private final BaseConfiguration config;
|
||||
@ -617,44 +585,6 @@ public abstract class BaseOptions {
|
||||
}
|
||||
},
|
||||
|
||||
new Option(resources, "-tag", 1) {
|
||||
@Override
|
||||
public boolean process(String opt, List<String> args) {
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
list.add(opt);
|
||||
list.add(args.get(0));
|
||||
customTagStrs.add(list);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
new Option(resources, "-taglet", 1) {
|
||||
@Override
|
||||
public boolean process(String opt, List<String> args) {
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
list.add(opt);
|
||||
list.add(args.get(0));
|
||||
customTagStrs.add(list);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
new Option(resources, "-tagletpath", 1) {
|
||||
@Override
|
||||
public boolean process(String opt, List<String> args) {
|
||||
tagletPath = args.get(0);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
new Option(resources, "--snippet-path", 1) {
|
||||
@Override
|
||||
public boolean process(String opt, List<String> args) {
|
||||
snippetPath = args.get(0);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
new Option(resources, "-version") {
|
||||
@Override
|
||||
public boolean process(String opt, List<String> args) {
|
||||
@ -705,14 +635,6 @@ public abstract class BaseOptions {
|
||||
disableJavaFxStrictChecks = true;
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
new Hidden(resources, "--show-taglets") {
|
||||
@Override
|
||||
public boolean process(String opt, List<String> args) {
|
||||
showTaglets = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
);
|
||||
return new TreeSet<>(options);
|
||||
@ -801,13 +723,6 @@ public abstract class BaseOptions {
|
||||
return copyDocfileSubdirs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Arguments for command-line option {@code -tag} and {@code -taglet}.
|
||||
*/
|
||||
LinkedHashSet<List<String>> customTagStrs() {
|
||||
return customTagStrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Argument for command-line option {@code --date}.
|
||||
*/
|
||||
@ -1023,20 +938,12 @@ public abstract class BaseOptions {
|
||||
* Generate author specific information for all the classes if @author
|
||||
* tag is used in the doc comment and if -author option is used.
|
||||
* <code>showauthor</code> is set to true if -author option is used.
|
||||
* Default is don't show author information.
|
||||
* Default is to not show author information.
|
||||
*/
|
||||
public boolean showAuthor() {
|
||||
return showAuthor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Argument for command-line option {@code --show-taglets}.
|
||||
* Show taglets (internal debug switch)
|
||||
*/
|
||||
public boolean showTaglets() {
|
||||
return showTaglets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Argument for command-line option {@code -version}.
|
||||
* Generate version specific information for the all the classes
|
||||
@ -1089,22 +996,6 @@ public abstract class BaseOptions {
|
||||
return summarizeOverriddenMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Argument for command-line option {@code -tagletpath}.
|
||||
* The path to Taglets
|
||||
*/
|
||||
public String tagletPath() {
|
||||
return tagletPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Argument for command-line option {@code --snippet-path}.
|
||||
* The path for external snippets.
|
||||
*/
|
||||
public String snippetPath() {
|
||||
return snippetPath;
|
||||
}
|
||||
|
||||
protected abstract static class Option implements Doclet.Option, Comparable<Option> {
|
||||
private final String[] names;
|
||||
private final String parameters;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -33,6 +33,12 @@ import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
|
||||
* The interface for writing class output.
|
||||
*/
|
||||
public interface ClassWriter {
|
||||
/**
|
||||
* Returns an instance of an output object.
|
||||
*
|
||||
* @return an instance of an output object
|
||||
*/
|
||||
Content getOutputInstance();
|
||||
|
||||
/**
|
||||
* Get the header of the page.
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -37,7 +37,6 @@ import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
|
||||
import jdk.javadoc.internal.doclets.toolkit.ClassWriter;
|
||||
import jdk.javadoc.internal.doclets.toolkit.CommentUtils;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
@ -143,7 +142,7 @@ public class ClassBuilder extends AbstractBuilder {
|
||||
* @throws DocletException if there is a problem while building the documentation
|
||||
*/
|
||||
protected void buildClassInfo(Content target) throws DocletException {
|
||||
Content c = new ContentBuilder();
|
||||
Content c = writer.getOutputInstance();
|
||||
buildParamInfo(c);
|
||||
buildSuperInterfacesInfo(c);
|
||||
buildImplementedInterfacesInfo(c);
|
||||
|
@ -24,7 +24,6 @@
|
||||
#
|
||||
|
||||
doclet.Generating_0=Generating {0}...
|
||||
doclet.Toolkit_Usage_Violation=The Doclet Toolkit can only be used by {0}
|
||||
doclet.MissingSerialTag=in class {0}, missing @serial tag for default serializable field: {1}.
|
||||
doclet.MissingSerialDataTag=in class {0}, missing @serialData tag in method {1}.
|
||||
doclet.Serializable_no_customization=No readObject or writeObject method declared.
|
||||
|
@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import javax.lang.model.element.Element;
|
||||
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import jdk.javadoc.doclet.Taglet.Location;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
|
||||
/**
|
||||
* A taglet that represents the {@code @deprecated} tag.
|
||||
*/
|
||||
public class DeprecatedTaglet extends BaseTaglet {
|
||||
|
||||
public DeprecatedTaglet() {
|
||||
super(DocTree.Kind.DEPRECATED, false,
|
||||
EnumSet.of(Location.MODULE, Location.TYPE, Location.CONSTRUCTOR, Location.METHOD, Location.FIELD));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getAllBlockTagOutput(Element holder, TagletWriter writer) {
|
||||
return writer.deprecatedTagOutput(holder);
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import javax.lang.model.element.Element;
|
||||
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import com.sun.source.doctree.LiteralTree;
|
||||
import jdk.javadoc.doclet.Taglet.Location;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
|
||||
/**
|
||||
* An inline taglet used to denote literal text.
|
||||
* For example, the text:
|
||||
* <blockquote> {@code {@literal a<B>c}} </blockquote>
|
||||
* displays as:
|
||||
* <blockquote> {@literal a<B>c} </blockquote>
|
||||
*/
|
||||
public class LiteralTaglet extends BaseTaglet {
|
||||
|
||||
LiteralTaglet() {
|
||||
super(DocTree.Kind.LITERAL, true, EnumSet.allOf(Location.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getInlineTagOutput(Element e, DocTree tag, TagletWriter writer) {
|
||||
return writer.literalTagOutput(e, (LiteralTree) tag);
|
||||
}
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import com.sun.source.doctree.SeeTree;
|
||||
import jdk.javadoc.doclet.Taglet.Location;
|
||||
import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocFinder.Result;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.Utils;
|
||||
|
||||
/**
|
||||
* A taglet that represents the {@code @see} tag.
|
||||
*/
|
||||
public class SeeTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
|
||||
public SeeTaglet() {
|
||||
super(DocTree.Kind.SEE, false, EnumSet.allOf(Location.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Output inherit(Element dst, Element src, DocTree tag, boolean isFirstSentence, BaseConfiguration configuration) {
|
||||
CommentHelper ch = configuration.utils.getCommentHelper(dst);
|
||||
var path = ch.getDocTreePath(tag);
|
||||
configuration.getMessages().warning(path, "doclet.inheritDocWithinInappropriateTag");
|
||||
return new Output(null, null, List.of(), true /* true, otherwise there will be an exception up the stack */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getAllBlockTagOutput(Element holder, TagletWriter writer) {
|
||||
Utils utils = writer.configuration().utils;
|
||||
List<? extends SeeTree> tags = utils.getSeeTrees(holder);
|
||||
Element e = holder;
|
||||
if (utils.isMethod(holder)) {
|
||||
var docFinder = utils.docFinder();
|
||||
Optional<Documentation> result = docFinder.search((ExecutableElement) holder,
|
||||
m -> Result.fromOptional(extract(utils, m))).toOptional();
|
||||
if (result.isPresent()) {
|
||||
ExecutableElement m = result.get().method();
|
||||
tags = utils.getSeeTrees(m);
|
||||
e = m;
|
||||
}
|
||||
}
|
||||
return writer.seeTagOutput(e, tags);
|
||||
}
|
||||
|
||||
private record Documentation(List<? extends SeeTree> seeTrees, ExecutableElement method) { }
|
||||
|
||||
private static Optional<Documentation> extract(Utils utils, ExecutableElement method) {
|
||||
List<? extends SeeTree> tags = utils.getSeeTrees(method);
|
||||
return tags.isEmpty() ? Optional.empty() : Optional.of(new Documentation(tags, method));
|
||||
}
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import com.sun.source.doctree.SpecTree;
|
||||
import jdk.javadoc.doclet.Taglet.Location;
|
||||
import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocFinder.Result;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.Utils;
|
||||
|
||||
/**
|
||||
* A taglet that represents the {@code @spec} tag.
|
||||
*/
|
||||
public class SpecTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
|
||||
public SpecTaglet() {
|
||||
super(DocTree.Kind.SPEC, false, EnumSet.allOf(Location.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Output inherit(Element dst, Element src, DocTree tag, boolean isFirstSentence, BaseConfiguration configuration) {
|
||||
CommentHelper ch = configuration.utils.getCommentHelper(dst);
|
||||
var path = ch.getDocTreePath(tag);
|
||||
configuration.getMessages().warning(path, "doclet.inheritDocWithinInappropriateTag");
|
||||
return new Output(null, null, List.of(), true /* true, otherwise there will be an exception up the stack */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content getAllBlockTagOutput(Element holder, TagletWriter writer) {
|
||||
Utils utils = writer.configuration().utils;
|
||||
List<? extends SpecTree> tags = utils.getSpecTrees(holder);
|
||||
Element e = holder;
|
||||
if (utils.isMethod(holder)) {
|
||||
var docFinder = utils.docFinder();
|
||||
Optional<Documentation> result = docFinder.search((ExecutableElement) holder,
|
||||
m -> Result.fromOptional(extract(utils, m))).toOptional();
|
||||
if (result.isPresent()) {
|
||||
e = result.get().method();
|
||||
tags = result.get().specTrees();
|
||||
}
|
||||
}
|
||||
return writer.specTagOutput(e, tags);
|
||||
}
|
||||
|
||||
private record Documentation(List<? extends SpecTree> specTrees, ExecutableElement method) { }
|
||||
|
||||
private static Optional<Documentation> extract(Utils utils, ExecutableElement method) {
|
||||
List<? extends SpecTree> tags = utils.getSpecTrees(method);
|
||||
return tags.isEmpty() ? Optional.empty() : Optional.of(new Documentation(tags, method));
|
||||
}
|
||||
}
|
@ -1,420 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import com.sun.source.doctree.SpecTree;
|
||||
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;
|
||||
import com.sun.source.doctree.SeeTree;
|
||||
import com.sun.source.doctree.SnippetTree;
|
||||
import com.sun.source.doctree.SystemPropertyTree;
|
||||
import com.sun.source.doctree.ThrowsTree;
|
||||
import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
import jdk.javadoc.internal.doclets.toolkit.taglets.Taglet.UnsupportedTagletOperationException;
|
||||
import jdk.javadoc.internal.doclets.toolkit.taglets.snippet.StyledText;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.Utils;
|
||||
|
||||
/**
|
||||
* The interface for the taglet writer.
|
||||
*/
|
||||
public abstract class TagletWriter {
|
||||
|
||||
/**
|
||||
* True if we only want to write the first sentence.
|
||||
*/
|
||||
protected final boolean isFirstSentence;
|
||||
|
||||
protected TagletWriter(boolean isFirstSentence) {
|
||||
this.isFirstSentence = isFirstSentence;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of an output object.
|
||||
*
|
||||
* @return an instance of an output object
|
||||
*/
|
||||
public abstract Content getOutputInstance();
|
||||
|
||||
/**
|
||||
* Returns the output for a {@code {@code ...}} tag.
|
||||
*
|
||||
* @param element The element that owns the doc comment
|
||||
* @param tag the tag
|
||||
*
|
||||
* @return the output
|
||||
*/
|
||||
protected abstract Content codeTagOutput(Element element, LiteralTree tag);
|
||||
|
||||
/**
|
||||
* Returns the output for a {@code {@index...}} tag.
|
||||
*
|
||||
* @param element The element that owns the doc comment
|
||||
* @param tag the tag
|
||||
*
|
||||
* @return the output
|
||||
*/
|
||||
protected abstract Content indexTagOutput(Element element, IndexTree tag);
|
||||
|
||||
/**
|
||||
* Returns the output for a {@code {@docRoot}} tag.
|
||||
*
|
||||
* @return the output
|
||||
*/
|
||||
protected abstract Content getDocRootOutput();
|
||||
|
||||
/**
|
||||
* Returns the output for a {@code @deprecated} tag.
|
||||
*
|
||||
* @param element The element that owns the doc comment
|
||||
*
|
||||
* @return the output
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param element The element that owns the doc comment
|
||||
* @param tag the tag
|
||||
*
|
||||
* @return the output
|
||||
*/
|
||||
protected abstract Content literalTagOutput(Element element, LiteralTree tag);
|
||||
|
||||
/**
|
||||
* Returns the header for the {@code @param} tags.
|
||||
*
|
||||
* @param kind the kind of header that is required
|
||||
*
|
||||
* @return the header
|
||||
*/
|
||||
protected abstract Content getParamHeader(ParamTaglet.ParamKind kind);
|
||||
|
||||
/**
|
||||
* Returns the output for a {@code @param} tag.
|
||||
* Note we cannot rely on the name in the tag, because we might be
|
||||
* inheriting the tag.
|
||||
*
|
||||
* @param element The element that owns the doc comment
|
||||
* @param paramTag the parameter to document
|
||||
* @param paramName the name of the parameter
|
||||
*
|
||||
* @return the output
|
||||
*/
|
||||
protected abstract Content paramTagOutput(Element element, ParamTree paramTag, String paramName);
|
||||
|
||||
/**
|
||||
* Returns the output for a {@code @return} tag.
|
||||
*
|
||||
* @param element the element that owns the doc comment
|
||||
* @param returnTag the return tag to document
|
||||
* @param inline whether this should be written as an inline instance or block instance
|
||||
*
|
||||
* @return the output
|
||||
*/
|
||||
protected abstract Content returnTagOutput(Element element, ReturnTree returnTag, boolean inline);
|
||||
|
||||
/**
|
||||
* Returns the output for {@code @see} tags.
|
||||
*
|
||||
* @param element The element that owns the doc comment
|
||||
* @param seeTags the list of tags
|
||||
*
|
||||
* @return the output
|
||||
*/
|
||||
protected abstract Content seeTagOutput(Element element, List<? extends SeeTree> seeTags);
|
||||
|
||||
/**
|
||||
* Returns the output for a series of simple tags.
|
||||
*
|
||||
* @param element The element that owns the doc comment
|
||||
* @param simpleTags the list of simple tags
|
||||
* @param header the header for the series of tags
|
||||
*
|
||||
* @return the output
|
||||
*/
|
||||
protected abstract Content simpleBlockTagOutput(Element element, List<? extends DocTree> simpleTags, String header);
|
||||
|
||||
/**
|
||||
* Returns the output for a {@code {@snippet ...}} tag.
|
||||
*
|
||||
* @param element The element that owns the doc comment
|
||||
* @param snippetTag the snippet tag
|
||||
* @param id the value of the id attribute, or null if not defined
|
||||
* @param lang the value of the lang attribute, or null if not defined
|
||||
*
|
||||
* @return the output
|
||||
*/
|
||||
protected abstract Content snippetTagOutput(Element element, SnippetTree snippetTag, StyledText text,
|
||||
String id, String lang);
|
||||
|
||||
/**
|
||||
* Returns the output for one or more {@code @spec} tags.
|
||||
*
|
||||
* @param element the element that owns the doc comment
|
||||
* @param specTags the array of @spec tags.
|
||||
*
|
||||
* @return the output
|
||||
*/
|
||||
protected abstract Content specTagOutput(Element element, List<? extends SpecTree> specTags);
|
||||
|
||||
/**
|
||||
* Returns the output for a {@code {@systemProperty...}} tag.
|
||||
*
|
||||
* @param element the element that owns the doc comment
|
||||
* @param systemPropertyTag the system property tag
|
||||
*
|
||||
* @return the output
|
||||
*/
|
||||
protected abstract Content systemPropertyTagOutput(Element element, SystemPropertyTree systemPropertyTag);
|
||||
|
||||
/**
|
||||
* Returns the header for the {@code @throws} tag.
|
||||
*
|
||||
* @return the header for the throws tag
|
||||
*/
|
||||
protected abstract Content getThrowsHeader();
|
||||
|
||||
/**
|
||||
* Returns the output for a default {@code @throws} tag.
|
||||
*
|
||||
* @param throwsType the type that is thrown
|
||||
* @param content the optional content to add as a description
|
||||
*
|
||||
* @return the output
|
||||
*/
|
||||
protected abstract Content throwsTagOutput(TypeMirror throwsType, Optional<Content> content);
|
||||
|
||||
/**
|
||||
* Returns the output for a {@code {@value}} tag.
|
||||
*
|
||||
* @param field the constant field that holds the value tag
|
||||
* @param constantVal the constant value to document
|
||||
* @param includeLink true if we should link the constant text to the
|
||||
* constant field itself
|
||||
*
|
||||
* @return the output
|
||||
*/
|
||||
protected abstract Content valueTagOutput(VariableElement field,
|
||||
String constantVal, boolean includeLink);
|
||||
|
||||
/**
|
||||
* Returns the output for an invalid tag. The returned content uses special styling to
|
||||
* highlight the problem. Depending on the presence of the {@code detail} string the method
|
||||
* returns a plain text span or an expandable component.
|
||||
*
|
||||
* @param summary the single-line summary message
|
||||
* @param detail the optional detail message which may contain preformatted text
|
||||
* @return the output
|
||||
*/
|
||||
protected abstract Content invalidTagOutput(String summary, Optional<String> detail);
|
||||
|
||||
/**
|
||||
* Returns the main type element of the current page or null for pages that don't have one.
|
||||
*
|
||||
* @return the type element of the current page or null.
|
||||
*/
|
||||
protected abstract TypeElement getCurrentPageElement();
|
||||
|
||||
/**
|
||||
* Returns the content generated from the block tags for a given element.
|
||||
* The content is generated according to the order of the list of taglets.
|
||||
* The result is a possibly-empty list of the output generated by each
|
||||
* of the given taglets for all of the tags they individually support.
|
||||
*
|
||||
* @param tagletManager the manager that manages the taglets
|
||||
* @param element the element that we are to write tags for
|
||||
* @param taglets the taglets for the tags to write
|
||||
*
|
||||
* @return the content
|
||||
*/
|
||||
public Content getBlockTagOutput(TagletManager tagletManager,
|
||||
Element element,
|
||||
List<Taglet> taglets) {
|
||||
for (Taglet t : taglets) {
|
||||
if (!t.isBlockTag()) {
|
||||
throw new IllegalArgumentException(t.getName());
|
||||
}
|
||||
}
|
||||
|
||||
Content output = getOutputInstance();
|
||||
Utils utils = configuration().utils;
|
||||
tagletManager.checkTags(element, utils.getBlockTags(element));
|
||||
tagletManager.checkTags(element, utils.getFullBody(element));
|
||||
for (Taglet taglet : taglets) {
|
||||
if (utils.isTypeElement(element) && taglet instanceof ParamTaglet) {
|
||||
// The type parameters and state components are documented in a special
|
||||
// section away from the tag info, so skip here.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (element.getKind() == ElementKind.MODULE && taglet instanceof BaseTaglet t) {
|
||||
switch (t.getTagKind()) {
|
||||
// @uses and @provides are handled separately, so skip here.
|
||||
// See ModuleWriterImpl.computeModulesData
|
||||
case USES:
|
||||
case PROVIDES:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (taglet instanceof DeprecatedTaglet) {
|
||||
//Deprecated information is documented "inline", not in tag info
|
||||
//section.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (taglet instanceof SimpleTaglet st && !st.enabled) {
|
||||
// taglet has been disabled
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
Content tagletOutput = taglet.getAllBlockTagOutput(element, this);
|
||||
if (tagletOutput != null) {
|
||||
tagletManager.seenTag(taglet.getName());
|
||||
output.add(tagletOutput);
|
||||
}
|
||||
} catch (UnsupportedTagletOperationException e) {
|
||||
// malformed taglet:
|
||||
// claims to support block tags (see Taglet.isBlockTag) but does not provide the
|
||||
// appropriate method, Taglet.getAllBlockTagOutput.
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content generated from an inline tag in the doc comment for a given element,
|
||||
* or {@code null} if the tag is not supported or does not return any output.
|
||||
*
|
||||
* @param holder the element associated with the doc comment
|
||||
* @param tagletManager the taglet manager for the current doclet
|
||||
* @param inlineTag the inline tag to be documented
|
||||
*
|
||||
* @return the content, or {@code null}
|
||||
*/
|
||||
public Content getInlineTagOutput(Element holder,
|
||||
TagletManager tagletManager,
|
||||
DocTree inlineTag) {
|
||||
|
||||
Map<String, Taglet> inlineTags = tagletManager.getInlineTaglets();
|
||||
CommentHelper ch = configuration().utils.getCommentHelper(holder);
|
||||
final String inlineTagName = ch.getTagName(inlineTag);
|
||||
Taglet t = inlineTags.get(inlineTagName);
|
||||
if (t == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
Content tagletOutput = t.getInlineTagOutput(holder, inlineTag, this);
|
||||
tagletManager.seenTag(t.getName());
|
||||
return tagletOutput;
|
||||
} catch (UnsupportedTagletOperationException e) {
|
||||
// malformed taglet:
|
||||
// claims to support inline tags (see Taglet.isInlineTag) but does not provide the
|
||||
// appropriate method, Taglet.getInlineTagOutput.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts inline tags and text to content, expanding the
|
||||
* inline tags along the way. Called wherever text can contain
|
||||
* an inline tag, such as in comments or in free-form text arguments
|
||||
* to block tags.
|
||||
*
|
||||
* @param holderTree the tree that holds the documentation
|
||||
* @param trees list of {@code DocTree} nodes containing text and inline tags (often alternating)
|
||||
* present in the text of interest for this doc
|
||||
*
|
||||
* @return the generated content
|
||||
*/
|
||||
public abstract Content commentTagsToOutput(DocTree holderTree, List<? extends DocTree> trees);
|
||||
|
||||
/**
|
||||
* Converts inline tags and text to content, expanding the
|
||||
* inline tags along the way. Called wherever text can contain
|
||||
* an inline tag, such as in comments or in free-form text arguments
|
||||
* to block tags.
|
||||
*
|
||||
* @param element The element that owns the documentation
|
||||
* @param trees list of {@code DocTree} nodes containing text and inline tags (often alternating)
|
||||
* present in the text of interest for this doc
|
||||
*
|
||||
* @return the generated content
|
||||
*/
|
||||
public abstract Content commentTagsToOutput(Element element, List<? extends DocTree> trees);
|
||||
|
||||
/**
|
||||
* Converts inline tags and text to content, expanding the
|
||||
* inline tags along the way. Called wherever text can contain
|
||||
* an inline tag, such as in comments or in free-form text arguments
|
||||
* to non-inline tags.
|
||||
*
|
||||
* @param element the element where comment resides
|
||||
* @param holder the tag that holds the documentation
|
||||
* @param trees array of text tags and inline tags (often alternating)
|
||||
* present in the text of interest for this doc
|
||||
* @param isFirstSentence true if this is the first sentence
|
||||
*
|
||||
* @return the generated content
|
||||
*/
|
||||
public abstract Content commentTagsToOutput(Element element, DocTree holder,
|
||||
List<? extends DocTree> trees, boolean isFirstSentence);
|
||||
|
||||
/**
|
||||
* Returns an instance of the configuration used for this doclet.
|
||||
*
|
||||
* @return an instance of the configuration used for this doclet
|
||||
*/
|
||||
public abstract BaseConfiguration configuration();
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This package has classes used to generate output for Javadoc tags.
|
||||
*
|
||||
* <p>Doclets no longer have to implement their own version of standard tags
|
||||
* such as @param and @throws. Individual taglets provide
|
||||
* common processing, independent of the output format.
|
||||
* Each doclet must have a taglet writer that takes a taglet
|
||||
* as input and writes doclet-dependent output. The taglet itself will
|
||||
* do the tag processing. For example, suppose we are outputting
|
||||
* @throws tags. The taglet would:
|
||||
* <ul>
|
||||
* <li> Retrieve the list of throws tags to be documented.
|
||||
* <li> Replace {@inheritDoc} with the appropriate documentation.
|
||||
* <li> Add throws documentation for exceptions that are declared in
|
||||
* the signature of the method but not documented with the throws tags.
|
||||
* </ul>
|
||||
* After doing the steps above, the taglet would pass the information to
|
||||
* the taglet writer for writing. The taglets are essentially builders for
|
||||
* tags.
|
||||
*/
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets;
|
@ -34,7 +34,6 @@ import java.text.ParseException;
|
||||
import java.text.RuleBasedCollator;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
@ -116,13 +115,12 @@ import com.sun.source.tree.LineMap;
|
||||
import com.sun.source.util.DocSourcePositions;
|
||||
import com.sun.source.util.DocTrees;
|
||||
import com.sun.source.util.TreePath;
|
||||
|
||||
import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
|
||||
import jdk.javadoc.internal.doclets.toolkit.BaseOptions;
|
||||
import jdk.javadoc.internal.doclets.toolkit.CommentUtils;
|
||||
import jdk.javadoc.internal.doclets.toolkit.CommentUtils.DocCommentInfo;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Resources;
|
||||
import jdk.javadoc.internal.doclets.toolkit.taglets.BaseTaglet;
|
||||
import jdk.javadoc.internal.doclets.toolkit.taglets.Taglet;
|
||||
|
||||
import static javax.lang.model.element.ElementKind.*;
|
||||
import static javax.lang.model.type.TypeKind.*;
|
||||
@ -991,7 +989,7 @@ public class Utils {
|
||||
/**
|
||||
* Return the type's dimension information, as a string.
|
||||
* <p>
|
||||
* For example, a two dimensional array of String returns "{@code [][]}".
|
||||
* For example, a two-dimensional array of String returns "{@code [][]}".
|
||||
*
|
||||
* @return the type's dimension information as a string.
|
||||
*/
|
||||
@ -2087,47 +2085,102 @@ public class Utils {
|
||||
commentHelperCache.remove(element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "raw" list of block tags from the doc-comment tree for an element,
|
||||
* or an empty list if there is no such comment.
|
||||
*
|
||||
* Note: The list may include {@code ErroneousTree} nodes.
|
||||
*
|
||||
* @param element the element
|
||||
* @return the list
|
||||
*/
|
||||
public List<? extends DocTree> getBlockTags(Element element) {
|
||||
return getBlockTags(getDocCommentTree(element));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "raw" list of block tags from a {@code DocCommentTree}, or an empty list
|
||||
* if the doc-comment tree is {@code null}.
|
||||
*
|
||||
* Note: The list may include {@code ErroneousTree} nodes.
|
||||
*
|
||||
* @param dcTree the doc-comment tree
|
||||
* @return the list
|
||||
*/
|
||||
public List<? extends DocTree> getBlockTags(DocCommentTree dcTree) {
|
||||
return dcTree == null ? List.of() : dcTree.getBlockTags();
|
||||
}
|
||||
|
||||
public List<? extends DocTree> getBlockTags(Element element, Predicate<DocTree> filter) {
|
||||
/**
|
||||
* Returns the list of block tags for the doc-comment tree for an element that match
|
||||
* a given filter, or an empty list if there is no such doc-comment.
|
||||
*
|
||||
* @param element the element
|
||||
* @param filter the filter
|
||||
* @return the list
|
||||
*/
|
||||
public List<? extends BlockTagTree> getBlockTags(Element element, Predicate<? super BlockTagTree> filter) {
|
||||
return getBlockTags(element).stream()
|
||||
.filter(t -> t.getKind() != ERRONEOUS)
|
||||
.map(t -> (BlockTagTree) t)
|
||||
.filter(filter)
|
||||
.toList();
|
||||
}
|
||||
|
||||
public <T extends DocTree> List<T> getBlockTags(Element element, Predicate<DocTree> filter, Class<T> tClass) {
|
||||
/**
|
||||
* Returns the list of block tags for the doc-comment tree for an element that match
|
||||
* a given filter, or an empty list if there is no such doc-comment.
|
||||
*
|
||||
* @param <T> the type of the required block tags
|
||||
* @param element the element
|
||||
* @param filter the filter
|
||||
* @return the list
|
||||
*/
|
||||
public <T extends BlockTagTree> List<T> getBlockTags(Element element,
|
||||
Predicate<? super BlockTagTree> filter,
|
||||
Class<T> tClass) {
|
||||
return getBlockTags(element).stream()
|
||||
.filter(t -> t.getKind() != ERRONEOUS)
|
||||
.map(t -> (BlockTagTree) t)
|
||||
.filter(filter)
|
||||
.map(tClass::cast)
|
||||
.toList();
|
||||
}
|
||||
|
||||
public List<? extends DocTree> getBlockTags(Element element, DocTree.Kind kind) {
|
||||
/**
|
||||
* Returns the list of block tags for the doc-comment tree for an element,
|
||||
* or an empty list if there is no such doc-comment.
|
||||
*
|
||||
* @param element the element
|
||||
* @return the list
|
||||
*/
|
||||
public List<? extends BlockTagTree> getBlockTags(Element element, DocTree.Kind kind) {
|
||||
return getBlockTags(element, t -> t.getKind() == kind);
|
||||
}
|
||||
|
||||
public <T extends DocTree> List<? extends T> getBlockTags(Element element, DocTree.Kind kind, Class<T> tClass) {
|
||||
/**
|
||||
* Returns the list of block tags for the doc-comment tree for an element that match a given kind,
|
||||
* or an empty list if there is no such doc-comment.
|
||||
*
|
||||
* @param <T> the type of the required block tags
|
||||
* @param element the element
|
||||
* @param kind the kind for the required block tags
|
||||
* @return the list
|
||||
*/
|
||||
public <T extends BlockTagTree> List<? extends T> getBlockTags(Element element, DocTree.Kind kind, Class<T> tClass) {
|
||||
return getBlockTags(element, t -> t.getKind() == kind, tClass);
|
||||
}
|
||||
|
||||
public List<? extends DocTree> getBlockTags(Element element, Taglet taglet) {
|
||||
return getBlockTags(element, t -> {
|
||||
if (taglet instanceof BaseTaglet baseTaglet) {
|
||||
return baseTaglet.accepts(t);
|
||||
} else if (t instanceof BlockTagTree blockTagTree) {
|
||||
return blockTagTree.getTagName().equals(taglet.getName());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
/**
|
||||
* Returns the list of block tags for the doc-comment tree for an element that match a given name,
|
||||
* or an empty list if there is no such doc-comment.
|
||||
*
|
||||
* @param element the element
|
||||
* @param tagName the name of the required block tags
|
||||
* @return the list
|
||||
*/
|
||||
public List<? extends BlockTagTree> getBlockTags(Element element, String tagName) {
|
||||
return getBlockTags(element, t -> t.getTagName().equals(tagName));
|
||||
}
|
||||
|
||||
public boolean hasBlockTag(Element element, DocTree.Kind kind) {
|
||||
|
Loading…
Reference in New Issue
Block a user