8200337: Generalize see and link tags for user-defined anchors
Reviewed-by: jjg
This commit is contained in:
parent
22347e46f7
commit
5622b09565
@ -106,16 +106,10 @@ import com.sun.tools.javac.resources.CompilerProperties.Errors;
|
||||
import com.sun.tools.javac.resources.CompilerProperties.Notes;
|
||||
import com.sun.tools.javac.resources.CompilerProperties.Warnings;
|
||||
import com.sun.tools.javac.tree.DCTree;
|
||||
import com.sun.tools.javac.tree.DCTree.DCBlockTag;
|
||||
import com.sun.tools.javac.tree.DCTree.DCComment;
|
||||
import com.sun.tools.javac.tree.DCTree.DCDocComment;
|
||||
import com.sun.tools.javac.tree.DCTree.DCEndPosTree;
|
||||
import com.sun.tools.javac.tree.DCTree.DCEntity;
|
||||
import com.sun.tools.javac.tree.DCTree.DCErroneous;
|
||||
import com.sun.tools.javac.tree.DCTree.DCIdentifier;
|
||||
import com.sun.tools.javac.tree.DCTree.DCParam;
|
||||
import com.sun.tools.javac.tree.DCTree.DCReference;
|
||||
import com.sun.tools.javac.tree.DCTree.DCText;
|
||||
import com.sun.tools.javac.tree.DocCommentTable;
|
||||
import com.sun.tools.javac.tree.DocTreeMaker;
|
||||
import com.sun.tools.javac.tree.EndPosTable;
|
||||
|
@ -410,11 +410,9 @@ public class DocCommentParser {
|
||||
* Matching pairs of {@literal < >} are skipped. The text is terminated by the first
|
||||
* unmatched }. It is an error if the beginning of the next tag is detected.
|
||||
*/
|
||||
// TODO: allowMember is currently ignored
|
||||
// TODO: boolean allowMember should be enum FORBID, ALLOW, REQUIRE
|
||||
// TODO: improve quality of parse to forbid bad constructions.
|
||||
@SuppressWarnings("fallthrough")
|
||||
protected DCReference reference(boolean allowMember) throws ParseException {
|
||||
protected DCReference reference(ReferenceParser.Mode mode) throws ParseException {
|
||||
int pos = bp;
|
||||
int depth = 0;
|
||||
|
||||
@ -468,13 +466,9 @@ public class DocCommentParser {
|
||||
|
||||
String sig = newString(pos, bp);
|
||||
|
||||
|
||||
try {
|
||||
ReferenceParser.Reference ref = new ReferenceParser(fac).parse(sig);
|
||||
return m.at(pos).newReferenceTree(sig,
|
||||
ref.moduleName, ref.qualExpr,
|
||||
ref.member, ref.paramTypes)
|
||||
.setEndPos(bp);
|
||||
ReferenceParser.Reference ref = new ReferenceParser(fac).parse(sig, mode);
|
||||
return m.at(pos).newReferenceTree(sig, ref).setEndPos(bp);
|
||||
} catch (ReferenceParser.ParseException pe) {
|
||||
throw new ParseException(pos + pe.pos, pe.getMessage());
|
||||
}
|
||||
@ -1237,7 +1231,7 @@ public class DocCommentParser {
|
||||
@Override
|
||||
public DCTree parse(int pos) throws ParseException {
|
||||
skipWhitespace();
|
||||
DCReference ref = reference(false);
|
||||
DCReference ref = reference(ReferenceParser.Mode.MEMBER_DISALLOWED);
|
||||
List<DCTree> description = blockContent();
|
||||
return m.at(pos).newExceptionTree(ref, description);
|
||||
}
|
||||
@ -1294,7 +1288,7 @@ public class DocCommentParser {
|
||||
new TagParser(TagParser.Kind.INLINE, DCTree.Kind.LINK) {
|
||||
@Override
|
||||
public DCTree parse(int pos) throws ParseException {
|
||||
DCReference ref = reference(true);
|
||||
DCReference ref = reference(ReferenceParser.Mode.MEMBER_OPTIONAL);
|
||||
List<DCTree> label = inlineContent();
|
||||
return m.at(pos).newLinkTree(ref, label);
|
||||
}
|
||||
@ -1304,7 +1298,7 @@ public class DocCommentParser {
|
||||
new TagParser(TagParser.Kind.INLINE, DCTree.Kind.LINK_PLAIN) {
|
||||
@Override
|
||||
public DCTree parse(int pos) throws ParseException {
|
||||
DCReference ref = reference(true);
|
||||
DCReference ref = reference(ReferenceParser.Mode.MEMBER_OPTIONAL);
|
||||
List<DCTree> label = inlineContent();
|
||||
return m.at(pos).newLinkPlainTree(ref, label);
|
||||
}
|
||||
@ -1351,7 +1345,7 @@ public class DocCommentParser {
|
||||
@Override
|
||||
public DCTree parse(int pos) throws ParseException {
|
||||
skipWhitespace();
|
||||
DCReference ref = reference(true);
|
||||
DCReference ref = reference(ReferenceParser.Mode.MEMBER_DISALLOWED);
|
||||
List<DCTree> description = blockContent();
|
||||
return m.at(pos).newProvidesTree(ref, description);
|
||||
}
|
||||
@ -1411,7 +1405,7 @@ public class DocCommentParser {
|
||||
|
||||
default:
|
||||
if (isJavaIdentifierStart(ch) || ch == '#') {
|
||||
DCReference ref = reference(true);
|
||||
DCReference ref = reference(ReferenceParser.Mode.MEMBER_OPTIONAL);
|
||||
List<DCTree> description = blockContent();
|
||||
return m.at(pos).newSeeTree(description.prepend(ref));
|
||||
}
|
||||
@ -1436,7 +1430,7 @@ public class DocCommentParser {
|
||||
skipWhitespace();
|
||||
DCIdentifier name = identifier();
|
||||
skipWhitespace();
|
||||
DCReference type = reference(false);
|
||||
DCReference type = reference(ReferenceParser.Mode.MEMBER_DISALLOWED);
|
||||
List<DCTree> description = null;
|
||||
if (isWhitespace(ch)) {
|
||||
skipWhitespace();
|
||||
@ -1606,7 +1600,7 @@ public class DocCommentParser {
|
||||
@Override
|
||||
public DCTree parse(int pos) throws ParseException {
|
||||
skipWhitespace();
|
||||
DCReference ref = reference(false);
|
||||
DCReference ref = reference(ReferenceParser.Mode.MEMBER_DISALLOWED);
|
||||
List<DCTree> description = blockContent();
|
||||
return m.at(pos).newThrowsTree(ref, description);
|
||||
}
|
||||
@ -1617,7 +1611,7 @@ public class DocCommentParser {
|
||||
@Override
|
||||
public DCTree parse(int pos) throws ParseException {
|
||||
skipWhitespace();
|
||||
DCReference ref = reference(true);
|
||||
DCReference ref = reference(ReferenceParser.Mode.MEMBER_DISALLOWED);
|
||||
List<DCTree> description = blockContent();
|
||||
return m.at(pos).newUsesTree(ref, description);
|
||||
}
|
||||
@ -1642,7 +1636,7 @@ public class DocCommentParser {
|
||||
format = null;
|
||||
}
|
||||
}
|
||||
DCReference ref = reference(true);
|
||||
DCReference ref = reference(ReferenceParser.Mode.MEMBER_REQUIRED);
|
||||
skipWhitespace();
|
||||
if (ch == '}') {
|
||||
nextChar();
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 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
|
||||
@ -30,7 +30,6 @@ import com.sun.source.tree.Tree;
|
||||
import com.sun.source.util.TreeScanner;
|
||||
import com.sun.tools.javac.parser.Tokens.TokenKind;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import com.sun.tools.javac.util.DiagnosticSource;
|
||||
import com.sun.tools.javac.util.JCDiagnostic;
|
||||
import com.sun.tools.javac.util.List;
|
||||
import com.sun.tools.javac.util.ListBuffer;
|
||||
@ -38,8 +37,6 @@ import com.sun.tools.javac.util.Log;
|
||||
import com.sun.tools.javac.util.Name;
|
||||
|
||||
import javax.tools.JavaFileObject;
|
||||
import java.util.Locale;
|
||||
import java.util.Queue;
|
||||
|
||||
/**
|
||||
* A utility class to parse a string in a doc comment containing a
|
||||
@ -51,6 +48,18 @@ import java.util.Queue;
|
||||
* deletion without notice.</b>
|
||||
*/
|
||||
public class ReferenceParser {
|
||||
|
||||
/**
|
||||
* Context dependent parsing mode which either disallows, allows or requires
|
||||
* a member reference. The <code>MEMBER_OPTIONAL</code> value also allows
|
||||
* arbitrary URI fragments using a double hash mark.
|
||||
*/
|
||||
public enum Mode {
|
||||
MEMBER_DISALLOWED,
|
||||
MEMBER_OPTIONAL,
|
||||
MEMBER_REQUIRED
|
||||
}
|
||||
|
||||
/**
|
||||
* An object to contain the result of parsing a reference to an API element.
|
||||
* Any, but not all, of the member fields may be null.
|
||||
@ -98,10 +107,11 @@ public class ReferenceParser {
|
||||
/**
|
||||
* Parse a reference to an API element as may be found in doc comment.
|
||||
* @param sig the signature to be parsed
|
||||
* @param mode the parsing mode
|
||||
* @return a {@code Reference} object containing the result of parsing the signature
|
||||
* @throws ParseException if there is an error while parsing the signature
|
||||
*/
|
||||
public Reference parse(String sig) throws ParseException {
|
||||
public Reference parse(String sig, Mode mode) throws ParseException {
|
||||
|
||||
// Break sig apart into moduleName qualifiedExpr member paramTypes.
|
||||
JCTree.JCExpression moduleName;
|
||||
@ -129,16 +139,28 @@ public class ReferenceParser {
|
||||
qualExpr = null;
|
||||
member = null;
|
||||
} else if (hash == -1) {
|
||||
if (lparen == -1) {
|
||||
if (lparen == -1 && mode != Mode.MEMBER_REQUIRED) {
|
||||
qualExpr = parseType(sig, afterSlash, sig.length(), dh);
|
||||
member = null;
|
||||
} else {
|
||||
if (mode == Mode.MEMBER_DISALLOWED) {
|
||||
throw new ParseException(hash, "dc.ref.unexpected.input");
|
||||
}
|
||||
qualExpr = null;
|
||||
member = parseMember(sig, afterSlash, lparen, dh);
|
||||
member = parseMember(sig, afterSlash, lparen > -1 ? lparen : sig.length(), dh);
|
||||
}
|
||||
} else {
|
||||
if (mode == Mode.MEMBER_DISALLOWED) {
|
||||
throw new ParseException(hash, "dc.ref.unexpected.input");
|
||||
}
|
||||
qualExpr = (hash == afterSlash) ? null : parseType(sig, afterSlash, hash, dh);
|
||||
if (lparen == -1) {
|
||||
if (sig.indexOf("#", afterHash) == afterHash) {
|
||||
// A hash symbol followed by another hash indicates a literal URL fragment.
|
||||
if (mode != Mode.MEMBER_OPTIONAL) {
|
||||
throw new ParseException(afterHash, "dc.ref.unexpected.input");
|
||||
}
|
||||
member = null;
|
||||
} else if (lparen == -1) {
|
||||
member = parseMember(sig, afterHash, sig.length(), dh);
|
||||
} else {
|
||||
member = parseMember(sig, afterHash, lparen, dh);
|
||||
|
@ -872,8 +872,8 @@ public abstract class DCTree implements DocTree {
|
||||
DCReference(String signature, JCTree.JCExpression moduleName, JCTree qualExpr, Name member, List<JCTree> paramTypes) {
|
||||
this.signature = signature;
|
||||
this.moduleName = moduleName;
|
||||
qualifierExpression = qualExpr;
|
||||
memberName = member;
|
||||
this.qualifierExpression = qualExpr;
|
||||
this.memberName = member;
|
||||
this.paramTypes = paramTypes;
|
||||
}
|
||||
|
||||
|
@ -350,8 +350,8 @@ public class DocTreeMaker implements DocTreeFactory {
|
||||
@Override @DefinedBy(Api.COMPILER_TREE)
|
||||
public DCReference newReferenceTree(String signature) {
|
||||
try {
|
||||
ReferenceParser.Reference ref = referenceParser.parse(signature);
|
||||
DCReference tree = new DCReference(signature, ref.moduleName, ref.qualExpr, ref.member, ref.paramTypes);
|
||||
ReferenceParser.Reference ref = referenceParser.parse(signature, ReferenceParser.Mode.MEMBER_OPTIONAL);
|
||||
DCReference tree = newReferenceTree(signature, ref);
|
||||
tree.pos = pos;
|
||||
return tree;
|
||||
} catch (ReferenceParser.ParseException e) {
|
||||
@ -359,8 +359,8 @@ public class DocTreeMaker implements DocTreeFactory {
|
||||
}
|
||||
}
|
||||
|
||||
public DCReference newReferenceTree(String signature, JCTree.JCExpression moduleName, JCTree qualExpr, Name member, List<JCTree> paramTypes) {
|
||||
DCReference tree = new DCReference(signature, moduleName, qualExpr, member, paramTypes);
|
||||
public DCReference newReferenceTree(String signature, ReferenceParser.Reference ref) {
|
||||
DCReference tree = new DCReference(signature, ref.moduleName, ref.qualExpr, ref.member, ref.paramTypes);
|
||||
tree.pos = pos;
|
||||
return tree;
|
||||
}
|
||||
|
@ -598,10 +598,21 @@ public class HtmlDocletWriter {
|
||||
/**
|
||||
* {@return the link to the given package}
|
||||
*
|
||||
* @param packageElement the package to link to.
|
||||
* @param label the label for the link.
|
||||
* @param packageElement the package to link to
|
||||
* @param label the label for the link
|
||||
*/
|
||||
public Content getPackageLink(PackageElement packageElement, Content label) {
|
||||
return getPackageLink(packageElement, label, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the link to the given package}
|
||||
*
|
||||
* @param packageElement the package to link to
|
||||
* @param label the label for the link
|
||||
* @param fragment the link fragment
|
||||
*/
|
||||
public Content getPackageLink(PackageElement packageElement, Content label, String fragment) {
|
||||
boolean included = packageElement != null && utils.isIncluded(packageElement);
|
||||
if (!included) {
|
||||
for (PackageElement p : configuration.packages) {
|
||||
@ -619,7 +630,7 @@ public class HtmlDocletWriter {
|
||||
}
|
||||
DocLink targetLink;
|
||||
if (included || packageElement == null) {
|
||||
targetLink = new DocLink(pathString(packageElement, DocPaths.PACKAGE_SUMMARY));
|
||||
targetLink = new DocLink(pathString(packageElement, DocPaths.PACKAGE_SUMMARY), fragment);
|
||||
} else {
|
||||
targetLink = getCrossPackageLink(packageElement);
|
||||
}
|
||||
@ -650,11 +661,23 @@ public class HtmlDocletWriter {
|
||||
* @param label tag for the link
|
||||
*/
|
||||
public Content getModuleLink(ModuleElement mdle, Content label) {
|
||||
return getModuleLink(mdle, label, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return a link to module}
|
||||
*
|
||||
* @param mdle the module being documented
|
||||
* @param label tag for the link
|
||||
* @param fragment the link fragment
|
||||
*/
|
||||
public Content getModuleLink(ModuleElement mdle, Content label, String fragment) {
|
||||
Set<ElementFlag> flags = mdle != null ? utils.elementFlags(mdle)
|
||||
: EnumSet.noneOf(ElementFlag.class);
|
||||
boolean included = utils.isIncluded(mdle);
|
||||
if (included) {
|
||||
DocLink targetLink = new DocLink(pathToRoot.resolve(docPaths.moduleSummary(mdle)));
|
||||
DocLink targetLink;
|
||||
targetLink = new DocLink(pathToRoot.resolve(docPaths.moduleSummary(mdle)), fragment);
|
||||
Content link = links.createLink(targetLink, label, "");
|
||||
if (flags.contains(ElementFlag.PREVIEW) && label != contents.moduleLabel) {
|
||||
link = new ContentBuilder(
|
||||
|
@ -501,24 +501,34 @@ public class TagletWriterImpl extends TagletWriter {
|
||||
CommentHelper ch = utils.getCommentHelper(holder);
|
||||
TypeElement refClass = ch.getReferencedClass(ref);
|
||||
Element refMem = ch.getReferencedMember(ref);
|
||||
String refMemName = ch.getReferencedMemberName(refSignature);
|
||||
String refFragment = ch.getReferencedFragment(refSignature);
|
||||
|
||||
if (refMemName == null && refMem != null) {
|
||||
refMemName = refMem.toString();
|
||||
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.
|
||||
reportWarning.accept("doclet.link.see.no_label", null);
|
||||
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);
|
||||
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())
|
||||
if (labelContent.isEmpty()) {
|
||||
labelContent = plainOrCode(isLinkPlain,
|
||||
Text.of(refPackage.getQualifiedName()));
|
||||
return htmlWriter.getPackageLink(refPackage, labelContent);
|
||||
}
|
||||
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);
|
||||
@ -541,8 +551,8 @@ public class TagletWriterImpl extends TagletWriter {
|
||||
Optional.of(labelContent.isEmpty() ? text: labelContent));
|
||||
}
|
||||
}
|
||||
} else if (refMemName == null) {
|
||||
// Must be a class reference since refClass is not null and refMemName is null.
|
||||
} 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)) {
|
||||
@ -555,9 +565,11 @@ public class TagletWriterImpl extends TagletWriter {
|
||||
return htmlWriter.getLink(new HtmlLinkInfo(configuration, HtmlLinkInfo.Kind.DEFAULT, refClass)
|
||||
.label(labelContent));
|
||||
} else if (refMem == null) {
|
||||
// Must be a member reference since refClass is not null and refMemName is not null.
|
||||
// However, refMem is null, so this referenced member does not exist.
|
||||
return (labelContent.isEmpty() ? text: labelContent);
|
||||
// This is a fragment reference since refClass and refFragment are not null but refMem is null.
|
||||
return htmlWriter.getLink(new HtmlLinkInfo(configuration, HtmlLinkInfo.Kind.SEE_TAG, refClass)
|
||||
.label(labelContent)
|
||||
.where(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.
|
||||
@ -591,6 +603,7 @@ public class TagletWriterImpl extends TagletWriter {
|
||||
}
|
||||
}
|
||||
}
|
||||
String refMemName = refFragment;
|
||||
if (configuration.currentTypeElement != containing) {
|
||||
refMemName = (utils.isConstructor(refMem))
|
||||
? refMemName
|
||||
|
@ -103,6 +103,7 @@ doclet.File_error=Error reading file: {0}
|
||||
doclet.URL_error=Error fetching URL: {0}
|
||||
doclet.Resource_error=Error reading resource: {0}
|
||||
doclet.link.no_reference=no reference given
|
||||
doclet.link.see.no_label=missing reference label
|
||||
doclet.see.class_or_package_not_found=Tag {0}: reference not found: {1}
|
||||
doclet.see.class_or_package_not_accessible=Tag {0}: reference not accessible: {1}
|
||||
doclet.see.nested_link=Tag {0}: nested link
|
||||
|
@ -208,7 +208,7 @@ public class CommentHelper {
|
||||
return (utils.isExecutableElement(e) || utils.isVariableElement(e)) ? e : null;
|
||||
}
|
||||
|
||||
public String getReferencedMemberName(String signature) {
|
||||
public String getReferencedFragment(String signature) {
|
||||
if (signature == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -244,12 +244,13 @@ public class DocPaths {
|
||||
}
|
||||
|
||||
/**
|
||||
* The path for the output directory for module documentation files.
|
||||
* Returns the path for a file within a module documentation output directory.
|
||||
* @param mdle the module
|
||||
* @return the path
|
||||
* @param path the path to append to the module path
|
||||
* @return the module documentation path
|
||||
*/
|
||||
public DocPath moduleDocFiles(ModuleElement mdle) {
|
||||
return createModulePath(mdle, "doc-files");
|
||||
public DocPath modulePath(ModuleElement mdle, String path) {
|
||||
return DocPath.create(mdle.getQualifiedName().toString()).resolve(path);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,300 @@
|
||||
/*
|
||||
* Copyright (c) 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8200337
|
||||
* @summary Generalize see and link tags for user-defined anchors
|
||||
* @library /tools/lib ../../lib
|
||||
* @modules
|
||||
* jdk.javadoc/jdk.javadoc.internal.tool
|
||||
* jdk.compiler/com.sun.tools.javac.api
|
||||
* jdk.compiler/com.sun.tools.javac.main
|
||||
* @build javadoc.tester.*
|
||||
* @run main TestSeeLinkAnchor
|
||||
*/
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import builder.ClassBuilder;
|
||||
import builder.ClassBuilder.*;
|
||||
import toolbox.ModuleBuilder;
|
||||
import toolbox.ToolBox;
|
||||
|
||||
import javadoc.tester.JavadocTester;
|
||||
|
||||
public class TestSeeLinkAnchor extends JavadocTester {
|
||||
|
||||
final ToolBox tb;
|
||||
private final Path src;
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
TestSeeLinkAnchor tester = new TestSeeLinkAnchor();
|
||||
tester.runTests(m -> new Object[]{Paths.get(m.getName())});
|
||||
}
|
||||
|
||||
TestSeeLinkAnchor() throws Exception {
|
||||
tb = new ToolBox();
|
||||
src = Paths.get("src");
|
||||
generateModuleSources();
|
||||
generatePackageSources();
|
||||
generateInvalidLinkSource();
|
||||
generateMissingLabelSource();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPackage(Path base) throws Exception {
|
||||
Path out = base.resolve("out");
|
||||
|
||||
javadoc("-d", out.toString(),
|
||||
"-sourcepath", src.toString(),
|
||||
"--no-platform-links",
|
||||
"p1", "p2");
|
||||
|
||||
checkExit(Exit.OK);
|
||||
checkOrder("p1/Class1.html",
|
||||
"""
|
||||
Link to <a href="../p2/package-summary.html#package-p2-heading"><code>heading in package p2</code></a>""",
|
||||
"""
|
||||
Plain link to <a href="../p2/Class2.html#class2-sub-heading">sub heading above</a></div>""",
|
||||
"""
|
||||
<li><a href="../p2/Class2.html#class2main"><code>See main heading in p2.Class2</code></a></li>
|
||||
<li><a href="../p2/package-summary.html#package-p2-heading"><code>See heading in p2</code></a></li>
|
||||
""");
|
||||
checkOrder("p2/Class2.html",
|
||||
"""
|
||||
Link to <a href="#class2main"><code>local anchor</code></a>""",
|
||||
"""
|
||||
Plain link <a href="../p1/Class1.html#main">to Class1</a>.""");
|
||||
checkOrder("p2/package-summary.html",
|
||||
"""
|
||||
<a href="Class2.html#class2-sub-heading"><code>See sub heading in p2.Class2</code></a>""");
|
||||
|
||||
checkOrder("p2/doc-files/file.html",
|
||||
"""
|
||||
Plain link to <a href="../../p1/Class1.html#main">heading in p1.ClassA</a>.""",
|
||||
"""
|
||||
<a href="../Class2.html#class2main"><code>See main heading in p2.ClassB</code></a>""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testModule(Path base) throws Exception {
|
||||
Path out = base.resolve("out");
|
||||
|
||||
javadoc("-d", out.toString(),
|
||||
"--module-source-path", src.toString(),
|
||||
"--no-platform-links",
|
||||
"--module", "m1,m2",
|
||||
"m2/com.m2");
|
||||
|
||||
checkExit(Exit.OK);
|
||||
checkOrder("m1/module-summary.html",
|
||||
"""
|
||||
<a href="../m2/com/m2/Class2.html#main-heading"><code>See main heading in Class2</code></a>""");
|
||||
checkOrder("m1/com/m1/Class1.html",
|
||||
"""
|
||||
<a href="../../../m2/com/m2/Class2.html#sub"><code>sub heading in Class2</code></a>.""",
|
||||
"""
|
||||
<li><a href="../../../m2/com/m2/Class2.html#main-heading"><code>See main heading in Class2</code></a></li>
|
||||
<li><a href="../../module-summary.html#module-m1-heading"><code>See heading in module m1</code></a></li>
|
||||
""");
|
||||
checkOrder("m2/com/m2/Class2.html",
|
||||
"""
|
||||
Link to <a href="../../../m1/module-summary.html#module-m1-heading"><code>heading in module m1</code></a>""",
|
||||
"""
|
||||
Plain link to <a href="#sub">sub heading above</a>.""");
|
||||
checkOrder("m2/doc-files/file.html",
|
||||
"""
|
||||
Link to <a href="../com/m2/Class2.html#main-heading"><code>heading in Class2</code></a>.""",
|
||||
"""
|
||||
<li><a href="../../m1/module-summary.html#module-m1-heading"><code>Heading in module m1</code></a></li>""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMissingLabel(Path base) throws Exception {
|
||||
Path out = base.resolve("out");
|
||||
|
||||
javadoc("-d", out.toString(),
|
||||
"-sourcepath", src.toString(),
|
||||
"--no-platform-links",
|
||||
"nolabel");
|
||||
|
||||
checkExit(Exit.OK);
|
||||
checkOutput(Output.OUT, true, """
|
||||
warning: missing reference label
|
||||
Link with missing label: {@link ##main}.
|
||||
^
|
||||
""",
|
||||
"""
|
||||
Class1.java:5: warning: missing reference label
|
||||
@see ##main
|
||||
^
|
||||
""");
|
||||
checkOutput("nolabel/Class1.html", true, """
|
||||
Link with missing label:\s
|
||||
<details class="invalid-tag">
|
||||
<summary>missing reference label</summary>
|
||||
<pre>##main</pre>
|
||||
</details>
|
||||
.</div>
|
||||
""",
|
||||
"""
|
||||
<details class="invalid-tag">
|
||||
<summary>missing reference label</summary>
|
||||
<pre>##main</pre>
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidLink(Path base) throws Exception {
|
||||
Path out = base.resolve("out");
|
||||
|
||||
javadoc("-d", out.toString(),
|
||||
"-sourcepath", src.toString(),
|
||||
"--no-platform-links",
|
||||
"inv");
|
||||
|
||||
checkExit(Exit.ERROR);
|
||||
checkOutput(Output.OUT, true, "error: reference not found");
|
||||
checkOutput("inv/Class1.html", true, """
|
||||
Invalid link to\s
|
||||
<details class="invalid-tag">
|
||||
<summary>invalid @link</summary>
|
||||
<pre><code>main heading</code></pre>
|
||||
</details>""");
|
||||
}
|
||||
|
||||
void generatePackageSources() throws Exception {
|
||||
MethodBuilder mb = MethodBuilder.parse("public String method(String s) { return s; }")
|
||||
.setComments("""
|
||||
@see p2.Class2##class2main See main heading in p2.Class2
|
||||
@see p2##package-p2-heading See heading in p2
|
||||
""");
|
||||
new ClassBuilder(tb, "p1.Class1")
|
||||
.setModifiers("public", "class")
|
||||
.setComments("""
|
||||
<h2 id="main">Class1 Main</h2>
|
||||
Link to {@link p2##package-p2-heading heading in package p2}
|
||||
<h3>Class1 Sub</h3>
|
||||
Plain link to {@linkplain p2.Class2##class2-sub-heading sub heading above}
|
||||
""")
|
||||
.addMembers(mb)
|
||||
.write(src);
|
||||
new ClassBuilder(tb, "p2.Class2")
|
||||
.setModifiers("public", "class")
|
||||
.setComments("""
|
||||
<h2 id="class2main">Class2 Main</h2>
|
||||
Link to {@link ##class2main local anchor}
|
||||
<h3>Class2 Sub</h3>
|
||||
Plain link {@linkplain p1.Class1##main to Class1}.
|
||||
""")
|
||||
.write(src);
|
||||
tb.writeFile(src.resolve("p2").resolve("package-info.java"),
|
||||
"""
|
||||
/**
|
||||
* <h2>Package p2</h2>
|
||||
*
|
||||
* @see p2.Class2##class2-sub-heading See sub heading in p2.Class2
|
||||
*/
|
||||
package p2;
|
||||
""");
|
||||
Path docFiles = src.resolve("p2").resolve("doc-files");
|
||||
tb.writeFile(docFiles.resolve("file.html"),
|
||||
"""
|
||||
<html>
|
||||
<head><title>Package p2 HTML File</title></head>
|
||||
<body><h1>Package p2 HTML File</h1>
|
||||
Plain link to {@linkplain p1.Class1##main heading in p1.ClassA}.
|
||||
@see p2.Class2##class2main See main heading in p2.ClassB
|
||||
</body>
|
||||
</html>
|
||||
""");
|
||||
}
|
||||
|
||||
void generateModuleSources() throws Exception {
|
||||
new ModuleBuilder(tb, "m1")
|
||||
.exports("com.m1")
|
||||
.classes("""
|
||||
package com.m1;
|
||||
/**
|
||||
* Link to the {@link m2/com.m2.Class2##sub sub heading in Class2}.
|
||||
*
|
||||
* @see m2/com.m2.Class2##main-heading See main heading in Class2
|
||||
* @see m1/##module-m1-heading See heading in module m1
|
||||
*/
|
||||
public class Class1 {}
|
||||
""")
|
||||
.comment("""
|
||||
<h2>Module m1</h2>
|
||||
@see m2/com.m2.Class2##main-heading See main heading in Class2
|
||||
""")
|
||||
.write(src);
|
||||
new ModuleBuilder(tb, "m2")
|
||||
.exports("com.m2")
|
||||
.classes("""
|
||||
package com.m2;
|
||||
/**
|
||||
* <h2>Main</h2>
|
||||
* Link to {@link m1/##module-m1-heading heading in module m1}
|
||||
*
|
||||
* <h3 id="sub">Sub</h3>
|
||||
* Plain link to {@linkplain Class2##sub sub heading above}.
|
||||
*/
|
||||
public class Class2 {}
|
||||
""")
|
||||
.write(src);
|
||||
Path docFiles = src.resolve("m2").resolve("doc-files");
|
||||
tb.writeFile(docFiles.resolve("file.html"),
|
||||
"""
|
||||
<html>
|
||||
<head><title>Module m2 HTML File</title></head>
|
||||
<body><h1>Module m2 HTML File</h1>
|
||||
Link to {@link com.m2.Class2##main-heading heading in Class2}.
|
||||
@see m1/##module-m1-heading Heading in module m1
|
||||
</body>
|
||||
</html>
|
||||
""");
|
||||
}
|
||||
|
||||
void generateInvalidLinkSource() throws Exception {
|
||||
new ClassBuilder(tb, "inv.Class1")
|
||||
.setModifiers("public", "class")
|
||||
.setComments("""
|
||||
<h2 id="main">Class1 Main</h2>
|
||||
Invalid link to {@link #main main heading}.
|
||||
""")
|
||||
.write(src);
|
||||
}
|
||||
|
||||
void generateMissingLabelSource() throws Exception {
|
||||
new ClassBuilder(tb, "nolabel.Class1")
|
||||
.setModifiers("public", "class")
|
||||
.setComments("""
|
||||
<h2 id="main">Class1 Main</h2>
|
||||
Link with missing label: {@link ##main}.
|
||||
@see ##main
|
||||
""")
|
||||
.write(src);
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 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
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 7021614 8273244
|
||||
* @bug 7021614 8273244 8200337
|
||||
* @summary extend com.sun.source API to support parsing javadoc comments
|
||||
* @modules jdk.compiler/com.sun.tools.javac.api
|
||||
* jdk.compiler/com.sun.tools.javac.file
|
||||
@ -191,6 +191,26 @@ DocComment[DOC_COMMENT, pos:1
|
||||
body: empty
|
||||
block tags: empty
|
||||
]
|
||||
*/
|
||||
|
||||
/**
|
||||
* abc {@linkplain java.lang.String##fragment desc} def
|
||||
*/
|
||||
void fragment_desc() { }
|
||||
/*
|
||||
DocComment[DOC_COMMENT, pos:1
|
||||
firstSentence: 3
|
||||
Text[TEXT, pos:1, abc_]
|
||||
Link[LINK_PLAIN, pos:5
|
||||
reference:
|
||||
Reference[REFERENCE, pos:17, java.lang.String##fragment]
|
||||
body: 1
|
||||
Text[TEXT, pos:44, desc]
|
||||
]
|
||||
Text[TEXT, pos:49, _def]
|
||||
body: empty
|
||||
block tags: empty
|
||||
]
|
||||
*/
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 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
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 7021614 8273244
|
||||
* @bug 7021614 8273244 8200337
|
||||
* @summary extend com.sun.source API to support parsing javadoc comments
|
||||
* @modules jdk.compiler/com.sun.tools.javac.api
|
||||
* jdk.compiler/com.sun.tools.javac.file
|
||||
@ -191,6 +191,26 @@ DocComment[DOC_COMMENT, pos:1
|
||||
body: empty
|
||||
block tags: empty
|
||||
]
|
||||
*/
|
||||
|
||||
/**
|
||||
* abc {@link java.lang.String##fragment desc} def
|
||||
*/
|
||||
void fragment_desc() { }
|
||||
/*
|
||||
DocComment[DOC_COMMENT, pos:1
|
||||
firstSentence: 3
|
||||
Text[TEXT, pos:1, abc_]
|
||||
Link[LINK, pos:5
|
||||
reference:
|
||||
Reference[REFERENCE, pos:12, java.lang.String##fragment]
|
||||
body: 1
|
||||
Text[TEXT, pos:39, desc]
|
||||
]
|
||||
Text[TEXT, pos:44, _def]
|
||||
body: empty
|
||||
block tags: empty
|
||||
]
|
||||
*/
|
||||
|
||||
}
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 7021614 8031212 8273244 8284908
|
||||
* @bug 7021614 8031212 8273244 8284908 8200337
|
||||
* @summary extend com.sun.source API to support parsing javadoc comments
|
||||
* @modules jdk.compiler/com.sun.tools.javac.api
|
||||
* jdk.compiler/com.sun.tools.javac.file
|
||||
@ -155,6 +155,25 @@ DocComment[DOC_COMMENT, pos:1
|
||||
Text[TEXT, pos:51, text]
|
||||
]
|
||||
]
|
||||
*/
|
||||
|
||||
/**
|
||||
* abc.
|
||||
* @see java.lang.String##fragment text
|
||||
*/
|
||||
void j_l_string_anchor() { }
|
||||
/*
|
||||
DocComment[DOC_COMMENT, pos:1
|
||||
firstSentence: 1
|
||||
Text[TEXT, pos:1, abc.]
|
||||
body: empty
|
||||
block tags: 1
|
||||
See[SEE, pos:7
|
||||
reference: 2
|
||||
Reference[REFERENCE, pos:12, java.lang.String##fragment]
|
||||
Text[TEXT, pos:39, text]
|
||||
]
|
||||
]
|
||||
*/
|
||||
|
||||
/**
|
||||
|
@ -72,6 +72,38 @@ DocComment[DOC_COMMENT, pos:1
|
||||
Text[TEXT, pos:27, f2_is_a_String]
|
||||
]
|
||||
]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @serialField field String#member f3 is a String
|
||||
*/
|
||||
String f3;
|
||||
/*
|
||||
DocComment[DOC_COMMENT, pos:1
|
||||
firstSentence: empty
|
||||
body: empty
|
||||
block tags: 1
|
||||
Erroneous[ERRONEOUS, pos:1, prefPos:26
|
||||
code: compiler.err.dc.ref.unexpected.input
|
||||
body: @serialField_fie...ld_String#member_f3_is_a_String
|
||||
]
|
||||
]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @serialField field String##fragment f4 is a String
|
||||
*/
|
||||
String f4;
|
||||
/*
|
||||
DocComment[DOC_COMMENT, pos:1
|
||||
firstSentence: empty
|
||||
body: empty
|
||||
block tags: 1
|
||||
Erroneous[ERRONEOUS, pos:1, prefPos:26
|
||||
code: compiler.err.dc.ref.unexpected.input
|
||||
body: @serialField_fie...ld_String##fragment_f4_is_a_String
|
||||
]
|
||||
]
|
||||
*/
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 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
|
||||
@ -69,5 +69,36 @@ DocComment[DOC_COMMENT, pos:1
|
||||
]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @throws Exception#member text
|
||||
*/
|
||||
void exception_member() throws Exception { }
|
||||
/*
|
||||
DocComment[DOC_COMMENT, pos:1
|
||||
firstSentence: empty
|
||||
body: empty
|
||||
block tags: 1
|
||||
Erroneous[ERRONEOUS, pos:1, prefPos:18
|
||||
code: compiler.err.dc.ref.unexpected.input
|
||||
body: @throws_Exception#member_text
|
||||
]
|
||||
]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @throws Exception##fragment text
|
||||
*/
|
||||
void exception_fragment() throws Exception { }
|
||||
/*
|
||||
DocComment[DOC_COMMENT, pos:1
|
||||
firstSentence: empty
|
||||
body: empty
|
||||
block tags: 1
|
||||
Erroneous[ERRONEOUS, pos:1, prefPos:18
|
||||
code: compiler.err.dc.ref.unexpected.input
|
||||
body: @throws_Exceptio...n##fragment_text
|
||||
]
|
||||
]
|
||||
*/
|
||||
}
|
||||
|
||||
|
@ -177,6 +177,42 @@ DocComment[DOC_COMMENT, pos:1
|
||||
body: empty
|
||||
block tags: empty
|
||||
]
|
||||
*/
|
||||
|
||||
/**
|
||||
* abc {@value java.awt.Color}
|
||||
*/
|
||||
int type_reference() { }
|
||||
/*
|
||||
DocComment[DOC_COMMENT, pos:1
|
||||
firstSentence: 3
|
||||
Text[TEXT, pos:1, abc_]
|
||||
Erroneous[ERRONEOUS, pos:5, prefPos:17
|
||||
code: compiler.err.dc.ref.unexpected.input
|
||||
body: {@value_java.awt.Color
|
||||
]
|
||||
Text[TEXT, pos:27, }]
|
||||
body: empty
|
||||
block tags: empty
|
||||
]
|
||||
*/
|
||||
|
||||
/**
|
||||
* abc {@value java.awt.Color##fragment}
|
||||
*/
|
||||
int anchor_reference() { }
|
||||
/*
|
||||
DocComment[DOC_COMMENT, pos:1
|
||||
firstSentence: 3
|
||||
Text[TEXT, pos:1, abc_]
|
||||
Erroneous[ERRONEOUS, pos:5, prefPos:28
|
||||
code: compiler.err.dc.ref.unexpected.input
|
||||
body: {@value_java.awt....Color##fragment
|
||||
]
|
||||
Text[TEXT, pos:37, }]
|
||||
body: empty
|
||||
block tags: empty
|
||||
]
|
||||
*/
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user