RFR [15] 8238598: Tighten up com.sun.source.doctree.DocTree API
Pavel Rappo
pavel.rappo at oracle.com
Fri Feb 7 12:18:31 UTC 2020
Hello,
Please review the change for https://bugs.openjdk.java.net/browse/JDK-8238598:
http://cr.openjdk.java.net/~prappo/8238598/webrev.00/
The crux of the proposed change is implementing and specifying semantically
dependent methods across the DocTree hierarchy of interfaces.
The design of the com.sun.source.doctree.DocTree API intentionally provides some
flexibility. Tree nodes can be interacted-with using an OOP style (the Visitor
design pattern), a structured programming style (boolean expressions on enum
constants), or a mixed style of `instanceof` checks.
Each subtype of the DocTree interface has these two methods:
<R, D> R accept(DocTreeVisitor<R, D> visitor, D data);
Kind getKind();
To process a tree node, you can implement visitor's visitXYZ methods of interest
and then pass that visitor to this node's `accept` method. Or, you can first
check this node's "flavor" explicitly by switching the result returned by
`getKind()` on a set of enum constants or using a series of `instanceof `
checks, and then based on this information, cast the node to a more specific
type.
For this to work, the kind and the type of a node must be firmly linked.
The expectation is that if there's the `XYZ` type of a tree node,
it has to return the `XYZe` enum constant from its `getKind` method
and vice versa [*].
That said, the implementations of the `accept` and `getKind` methods are
currently deferred until concrete classes, com.sun.tools.javac.tree.DCTree, and
their specification is absent. The proposed patch tries to address both issues
by moving the said methods' implementation to the most specific interfaces and
specifying their behavior. This collocates and ties both methods in a more
explicit way.
I cannot think of any likely compatibility risks caused by introducing default
methods in this particular case. Still, if this is approved I will file a CSR.
I'm also aware that if approved the DocTree API will become less consistent with
the com.sun.source.tree.Tree API which it has been modeled after. It would be
nice to get feedback from the original designers of those two APIs.
I couldn't help cleaning up some of the javadoc comments issues along the way.
This includes typos, formatting, and non-normative clarifications.
Thanks,
-Pavel
-------------------------------------------------------------------------------
[*] The mapping from the Kind enum to the subtype of the DocTree interface is
not bidirectional. Some of the tags are modeled using the same interface.\
For example, @throws and @exception, @link and @linkplain, @code and @literal,
share the ThrowsTree, LinkTree, and LiteralTree interfaces respectively. When
dispatching the call to the visitor, sharing tags use the same method.
The visitor then might need to resolve the ambiguity by additionally querying
the kind of the node it visits. For example,
@Override
public Boolean visitLiteral(LiteralTree node, Content c) {
String s = node.getBody().getBody();
Content content = new StringContent(utils.normalizeNewlines(s));
* if (node.getKind() == CODE)
content = HtmlTree.CODE(content);
result.add(content);
return false;
}
Thus, the visitor or the `instanceof` alone is not enough. I'm not sure why it
was done like that, but as a thought experiment, we could consider what it would
take to split those 3 pairs of shared tags in a compatible way.
If we were to introduce, say, the `CodeTree` type, we would have to do the
following (most of the javadoc comments are not included for brevity).
1. Create a new subinterface of `LiteralTree`:
public interface CodeTree extends LiteralTree {
/**
* Returns {@link Kind#CODE}.
* @return {@link Kind#CODE}
*/
@Override
default Kind getKind() {
return Kind.CODE;
}
/**
* @implSpec This implementation calls {@code visitor.visitCode(this, data)}.
*/
@Override
default <R, D> R accept(DocTreeVisitor<R, D> visitor, D data) {
return visitor.visitCode(this, data);
}
}
Why is `CodeTree` a subinterface of `LiteralTree` rather than a sibling
(i.e. `CodeTree extends InlineTagTree`)? Because of compatibility with existing
implementations that might use `instanceof` instead of `getKind()`.
2. Introduce a new method in `DocTreeVisitor`, which by default forwards the
calls to the old path:
@Override
default R visitCode(CodeTree node, P data) {
return visitLiteral(node, data);
}
More information about the compiler-dev
mailing list