8286101: Support formatting in @value tag
Reviewed-by: prappo
This commit is contained in:
parent
8f400b9aab
commit
53a0acee06
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 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,6 +30,7 @@ package com.sun.source.doctree;
|
||||
*
|
||||
* <pre>
|
||||
* {@value reference}
|
||||
* {@value format reference}
|
||||
* </pre>
|
||||
*
|
||||
* @since 1.8
|
||||
@ -40,4 +41,16 @@ public interface ValueTree extends InlineTagTree {
|
||||
* @return the reference
|
||||
*/
|
||||
ReferenceTree getReference();
|
||||
|
||||
/**
|
||||
* Returns the format string, or {@code null} if none was provided.
|
||||
*
|
||||
* @return the format string
|
||||
*
|
||||
* @implSpec This implementation returns {@code null}.
|
||||
* @since 19
|
||||
*/
|
||||
default TextTree getFormat() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 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
|
||||
@ -413,6 +413,19 @@ public interface DocTreeFactory {
|
||||
*/
|
||||
ValueTree newValueTree(ReferenceTree ref);
|
||||
|
||||
/**
|
||||
* Creates a new {@code ValueTree} object, to represent a {@code {@value }} tag.
|
||||
* @param format a format string for the value
|
||||
* @param ref a reference to the value
|
||||
* @return a {@code ValueTree} object
|
||||
*
|
||||
* @implSpec This implementation calls {@link #newValueTree(ReferenceTree) newValueTree(ref)}.
|
||||
* @since 19
|
||||
*/
|
||||
default ValueTree newValueTree(TextTree format, ReferenceTree ref) {
|
||||
return newValueTree(ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code VersionTree} object, to represent a {@code {@version }} tag.
|
||||
* @param text the content of the tag
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 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
|
||||
@ -638,7 +638,9 @@ public class DocTreeScanner<R,P> implements DocTreeVisitor<R,P> {
|
||||
*/
|
||||
@Override
|
||||
public R visitValue(ValueTree node, P p) {
|
||||
return scan(node.getReference(), p);
|
||||
R r = scan(node.getFormat(), p);
|
||||
r = scanAndReduce(node.getReference(), p, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1579,15 +1579,32 @@ public class DocCommentParser {
|
||||
}
|
||||
},
|
||||
|
||||
// {@value package.class#field}
|
||||
// {@value [format-string] package.class#field}
|
||||
new TagParser(TagParser.Kind.INLINE, DCTree.Kind.VALUE) {
|
||||
@Override
|
||||
public DCTree parse(int pos) throws ParseException {
|
||||
skipWhitespace();
|
||||
DCText format;
|
||||
switch (ch) {
|
||||
case '%' -> {
|
||||
format = inlineWord();
|
||||
skipWhitespace();
|
||||
}
|
||||
case '"' -> {
|
||||
format = quotedString();
|
||||
skipWhitespace();
|
||||
}
|
||||
default -> {
|
||||
format = null;
|
||||
}
|
||||
}
|
||||
DCReference ref = reference(true);
|
||||
skipWhitespace();
|
||||
if (ch == '}') {
|
||||
nextChar();
|
||||
return m.at(pos).newValueTree(ref);
|
||||
return format == null
|
||||
? m.at(pos).newValueTree(ref)
|
||||
: m.at(pos).newValueTree(format, ref);
|
||||
}
|
||||
nextChar();
|
||||
throw new ParseException("dc.unexpected.content");
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 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
|
||||
@ -1310,9 +1310,11 @@ public abstract class DCTree implements DocTree {
|
||||
}
|
||||
|
||||
public static class DCValue extends DCInlineTag implements ValueTree {
|
||||
public final DCText format;
|
||||
public final DCReference ref;
|
||||
|
||||
DCValue(DCReference ref) {
|
||||
DCValue(DCText format, DCReference ref) {
|
||||
this.format = format;
|
||||
this.ref = ref;
|
||||
}
|
||||
|
||||
@ -1326,6 +1328,11 @@ public abstract class DCTree implements DocTree {
|
||||
return v.visitValue(this, d);
|
||||
}
|
||||
|
||||
@Override @DefinedBy(Api.COMPILER_TREE)
|
||||
public TextTree getFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
@Override @DefinedBy(Api.COMPILER_TREE)
|
||||
public ReferenceTree getReference() {
|
||||
return ref;
|
||||
|
@ -632,6 +632,10 @@ public class DocPretty implements DocTreeVisitor<Void,Void> {
|
||||
try {
|
||||
print("{");
|
||||
printTagName(node);
|
||||
if (node.getFormat() != null) {
|
||||
print(" ");
|
||||
print(node.getFormat());
|
||||
}
|
||||
if (node.getReference() != null) {
|
||||
print(" ");
|
||||
print(node.getReference());
|
||||
|
@ -480,8 +480,13 @@ public class DocTreeMaker implements DocTreeFactory {
|
||||
|
||||
@Override @DefinedBy(Api.COMPILER_TREE)
|
||||
public DCValue newValueTree(ReferenceTree ref) {
|
||||
return newValueTree(null, ref);
|
||||
}
|
||||
|
||||
@Override @DefinedBy(Api.COMPILER_TREE)
|
||||
public DCValue newValueTree(TextTree format, ReferenceTree ref) {
|
||||
// TODO: verify the reference is to a constant value
|
||||
DCValue tree = new DCValue((DCReference) ref);
|
||||
DCValue tree = new DCValue((DCText) format, (DCReference) ref);
|
||||
tree.pos = pos;
|
||||
return tree;
|
||||
}
|
||||
|
@ -123,6 +123,17 @@ public class Messages {
|
||||
report(ERROR, fo, start, pos, end, resources.getText(key, args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports an error message to the doclet's reporter.
|
||||
*
|
||||
* @param e an element identifying the position to be included with the message
|
||||
* @param key the name of a resource containing the message to be printed
|
||||
* @param args optional arguments to be replaced in the message
|
||||
*/
|
||||
public void error(Element e, String key, Object... args) {
|
||||
report(ERROR, e, resources.getText(key, args));
|
||||
}
|
||||
|
||||
// ***** Warnings *****
|
||||
|
||||
/**
|
||||
|
@ -203,6 +203,7 @@ doclet.Groupname_already_used=In -group option, groupname already used: {0}
|
||||
doclet.value_tag_invalid_reference={0} (referenced by @value tag) is an unknown reference.
|
||||
doclet.value_tag_invalid_constant=@value tag (which references {0}) can only be used in constants.
|
||||
doclet.value_tag_invalid_use=@value tag cannot be used here.
|
||||
doclet.value_tag_invalid_format=invalid format: {0}
|
||||
doclet.dest_dir_create=Creating destination directory: "{0}"
|
||||
doclet.in={0} in {1}
|
||||
doclet.Fields=Fields
|
||||
|
@ -26,10 +26,14 @@
|
||||
package jdk.javadoc.internal.doclets.toolkit.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.internal.doclets.toolkit.BaseConfiguration;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
@ -95,8 +99,27 @@ public class ValueTaglet extends BaseTaglet {
|
||||
"doclet.value_tag_invalid_reference", tag.toString());
|
||||
}
|
||||
} else if (field.getConstantValue() != null) {
|
||||
TextTree format = ((ValueTree) tag).getFormat();
|
||||
String text;
|
||||
if (format != null) {
|
||||
String f = format.getBody();
|
||||
if (f.startsWith("\"")) {
|
||||
f = f.substring(1, f.length() - 1);
|
||||
}
|
||||
try {
|
||||
text = String.format(configuration.getLocale(), f, field.getConstantValue());
|
||||
} catch (IllegalFormatException e) {
|
||||
messages.error(holder,
|
||||
"doclet.value_tag_invalid_format", format);
|
||||
return writer.invalidTagOutput(
|
||||
messages.getResources().getText("doclet.value_tag_invalid_format", format),
|
||||
Optional.empty());
|
||||
}
|
||||
} else {
|
||||
text = utils.constantValueExpression(field);
|
||||
}
|
||||
return writer.valueTagOutput(field,
|
||||
utils.constantValueExpression(field),
|
||||
text,
|
||||
// TODO: investigate and cleanup
|
||||
// in the j.l.m world, equals will not be accurate
|
||||
// !field.equals(tag.holder())
|
||||
|
@ -1115,6 +1115,16 @@ public class Checker extends DocTreePathScanner<Void, Void> {
|
||||
if (!isConstant(e))
|
||||
env.messages.error(REFERENCE, tree, "dc.value.not.a.constant");
|
||||
}
|
||||
TextTree format = tree.getFormat();
|
||||
if (format != null) {
|
||||
String f = format.getBody().toString();
|
||||
long count = format.getBody().toString().chars()
|
||||
.filter(ch -> ch == '%')
|
||||
.count();
|
||||
if (count != 1) {
|
||||
env.messages.error(REFERENCE, format, "dc.value.bad.format", f);
|
||||
}
|
||||
}
|
||||
|
||||
markEnclosingTag(Flag.HAS_INLINE_TAG);
|
||||
return super.visitValue(tree, ignore);
|
||||
|
@ -88,6 +88,7 @@ dc.tag.unknown = unknown tag: {0}
|
||||
dc.tag.not.supported.html5 = tag not supported in HTML5: {0}
|
||||
dc.text.not.allowed = text not allowed in <{0}> element
|
||||
dc.unexpected.comment=documentation comment not expected here
|
||||
dc.value.bad.format=invalid format: {0}
|
||||
dc.value.not.allowed.here='{@value} not allowed here
|
||||
dc.value.not.a.constant=value does not refer to a constant
|
||||
|
||||
|
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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 8286101
|
||||
* @summary Support formatting in at-value tag
|
||||
* @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 TestValueFormats
|
||||
*/
|
||||
|
||||
|
||||
import javadoc.tester.JavadocTester;
|
||||
import toolbox.ModuleBuilder;
|
||||
import toolbox.ToolBox;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class TestValueFormats extends JavadocTester {
|
||||
|
||||
final ToolBox tb;
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
TestValueFormats tester = new TestValueFormats();
|
||||
tester.runTests(m -> new Object[]{Paths.get(m.getName())});
|
||||
}
|
||||
|
||||
TestValueFormats() {
|
||||
tb = new ToolBox();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValid(Path base) throws Exception {
|
||||
Path srcDir = base.resolve("src");
|
||||
tb.writeJavaFiles(srcDir,
|
||||
"""
|
||||
package p;
|
||||
/**
|
||||
* Comment.
|
||||
*/
|
||||
public class C {
|
||||
/** The value {@value} is {@value %4x} or {@value "0x%04x"}. */
|
||||
public static final int i65535 = 65535;
|
||||
/** The value {@value} is {@value %5.2f}. */
|
||||
public static final double pi = 3.1415926525;
|
||||
}""");
|
||||
|
||||
Path outDir = base.resolve("out");
|
||||
javadoc("-d", outDir.toString(),
|
||||
"-sourcepath", srcDir.toString(),
|
||||
"p");
|
||||
|
||||
checkExit(Exit.OK);
|
||||
|
||||
checkOutput("p/C.html", true,
|
||||
"""
|
||||
<h3>i65535</h3>
|
||||
<div class="member-signature"><span class="modifiers">public static final</span> <span clas\
|
||||
s="return-type">int</span> <span class="element-name">i65535</span></div>
|
||||
<div class="block">The value 65535 is ffff or 0xffff.</div>""",
|
||||
"""
|
||||
<h3>pi</h3>
|
||||
<div class="member-signature"><span class="modifiers">public static final</span> <span class="return-type">double</span> <span class="element-name">pi</span></div>
|
||||
<div class="block">The value 3.1415926525 is 3.14.</div>""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadFormat(Path base) throws Exception {
|
||||
Path srcDir = base.resolve("src");
|
||||
tb.writeJavaFiles(srcDir,
|
||||
"""
|
||||
package p;
|
||||
/**
|
||||
* Comment.
|
||||
*/
|
||||
public class C {
|
||||
/** The value {@value} is {@value %a}. */
|
||||
public static final int i65535 = 65535;
|
||||
}""");
|
||||
|
||||
Path outDir = base.resolve("out");
|
||||
javadoc("-d", outDir.toString(),
|
||||
"-sourcepath", srcDir.toString(),
|
||||
"p");
|
||||
|
||||
checkExit(Exit.ERROR);
|
||||
|
||||
checkOutput("p/C.html", true,
|
||||
"""
|
||||
<h3>i65535</h3>
|
||||
<div class="member-signature"><span class="modifiers">public static final</span> <span class="return-type">int</span> <span class="element-name">i65535</span></div>
|
||||
<div class="block">The value 65535 is <span class="invalid-tag">invalid format: %a</span>.</div>""");
|
||||
}
|
||||
}
|
@ -65,4 +65,16 @@ public class ValueTest {
|
||||
|
||||
/** invalid enum constant: {@value Thread.State#NEW} */
|
||||
public int badEnum;
|
||||
|
||||
/** valid: {@value %04x} */
|
||||
public static final int maxShort = 65535;
|
||||
|
||||
/** valid: {@value "%5.2f"} */
|
||||
public static final double pi = 3.14159265358979323846;
|
||||
|
||||
/** invalid format: {@value %%04x} */
|
||||
public static final int f3 = 0;
|
||||
|
||||
/** invalid format: {@value "04x"} */
|
||||
public static final int f4 = 0;
|
||||
}
|
||||
|
@ -19,4 +19,10 @@ ValueTest.java:63: error: value does not refer to a constant
|
||||
ValueTest.java:66: error: value does not refer to a constant
|
||||
/** invalid enum constant: {@value Thread.State#NEW} */
|
||||
^
|
||||
7 errors
|
||||
ValueTest.java:75: error: invalid format: %%04x
|
||||
/** invalid format: {@value %%04x} */
|
||||
^
|
||||
ValueTest.java:78: error: invalid format: "04x"
|
||||
/** invalid format: {@value "04x"} */
|
||||
^
|
||||
9 errors
|
||||
|
@ -699,6 +699,7 @@ public class DocCommentTester {
|
||||
public Void visitValue(ValueTree node, Void p) {
|
||||
header(node);
|
||||
indent(+1);
|
||||
print("format", node.getFormat());
|
||||
print("reference", node.getReference());
|
||||
indent(-1);
|
||||
indent();
|
||||
|
@ -43,6 +43,7 @@ DocComment[DOC_COMMENT, pos:1
|
||||
firstSentence: 2
|
||||
Text[TEXT, pos:1, abc_]
|
||||
Value[VALUE, pos:5
|
||||
format: null
|
||||
reference: null
|
||||
]
|
||||
body: empty
|
||||
@ -59,6 +60,7 @@ DocComment[DOC_COMMENT, pos:1
|
||||
firstSentence: 2
|
||||
Text[TEXT, pos:1, abc_]
|
||||
Value[VALUE, pos:5
|
||||
format: null
|
||||
reference:
|
||||
Reference[REFERENCE, pos:13, java.awt.Color#RED]
|
||||
]
|
||||
@ -76,6 +78,7 @@ DocComment[DOC_COMMENT, pos:1
|
||||
firstSentence: 2
|
||||
Text[TEXT, pos:1, abc_]
|
||||
Value[VALUE, pos:5
|
||||
format: null
|
||||
reference:
|
||||
Reference[REFERENCE, pos:13, java.awt.Color#RED]
|
||||
]
|
||||
@ -102,6 +105,79 @@ DocComment[DOC_COMMENT, pos:1
|
||||
]
|
||||
*/
|
||||
|
||||
/**
|
||||
* abc {@value %d java.awt.Color#RED}
|
||||
*/
|
||||
int format_plain() { }
|
||||
/*
|
||||
DocComment[DOC_COMMENT, pos:1
|
||||
firstSentence: 2
|
||||
Text[TEXT, pos:1, abc_]
|
||||
Value[VALUE, pos:5
|
||||
format:
|
||||
Text[TEXT, pos:13, %d]
|
||||
reference:
|
||||
Reference[REFERENCE, pos:16, java.awt.Color#RED]
|
||||
]
|
||||
body: empty
|
||||
block tags: empty
|
||||
]
|
||||
*/
|
||||
|
||||
/**
|
||||
* abc {@value "%d" java.awt.Color#RED}
|
||||
*/
|
||||
int format_quoted() { }
|
||||
/*
|
||||
DocComment[DOC_COMMENT, pos:1
|
||||
firstSentence: 2
|
||||
Text[TEXT, pos:1, abc_]
|
||||
Value[VALUE, pos:5
|
||||
format:
|
||||
Text[TEXT, pos:13, "%d"]
|
||||
reference:
|
||||
Reference[REFERENCE, pos:18, java.awt.Color#RED]
|
||||
]
|
||||
body: empty
|
||||
block tags: empty
|
||||
]
|
||||
*/
|
||||
|
||||
/**
|
||||
* abc {@value 0x%x4 java.awt.Color#RED}
|
||||
*/
|
||||
int format_invalid() { }
|
||||
/*
|
||||
DocComment[DOC_COMMENT, pos:1
|
||||
firstSentence: 3
|
||||
Text[TEXT, pos:1, abc_]
|
||||
Erroneous[ERRONEOUS, pos:5, prefPos:13
|
||||
code: compiler.err.dc.ref.unexpected.input
|
||||
body: {@value_0x%x4
|
||||
]
|
||||
Text[TEXT, pos:18, _java.awt.Color#RED}]
|
||||
body: empty
|
||||
block tags: empty
|
||||
]
|
||||
*/
|
||||
|
||||
/**
|
||||
* abc {@value "%d" java.awt.Color#RED junk}
|
||||
*/
|
||||
int format_trailing_junk() { }
|
||||
/*
|
||||
DocComment[DOC_COMMENT, pos:1
|
||||
firstSentence: 3
|
||||
Text[TEXT, pos:1, abc_]
|
||||
Erroneous[ERRONEOUS, pos:5, prefPos:37
|
||||
code: compiler.err.dc.unexpected.content
|
||||
body: {@value_"%d"_jav...a.awt.Color#RED_j
|
||||
]
|
||||
Text[TEXT, pos:38, unk}]
|
||||
body: empty
|
||||
block tags: empty
|
||||
]
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, 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
|
||||
@ -1186,6 +1186,7 @@ public class DPrinter {
|
||||
}
|
||||
|
||||
public Void visitValue(ValueTree node, Void p) {
|
||||
printDocTree("format", node.getFormat());
|
||||
printDocTree("value", node.getReference());
|
||||
return visitInlineTag(node, null);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user