8009724: Enhance the DocTree API with DocTreePath

Adding DocTreePath and DocTreePathScanner similar to TreePath and TreePathScanner, respectively

Co-authored-by: Ralph Benjamin Ruijs <ralphbenjamin@netbeans.org>
Reviewed-by: jjg
This commit is contained in:
Jan Lahoda 2013-05-06 16:22:45 +02:00
parent d606b9d0d6
commit 57d8a7fc0f
7 changed files with 486 additions and 50 deletions

View File

@ -0,0 +1,171 @@
/*
* Copyright (c) 2006, 2013, 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 com.sun.source.util;
import com.sun.source.doctree.DocCommentTree;
import com.sun.source.doctree.DocTree;
import java.util.Iterator;
/**
* A path of tree nodes, typically used to represent the sequence of ancestor
* nodes of a tree node up to the top level DocCommentTree node.
*
* @since 1.8
*/
@jdk.Supported
public class DocTreePath implements Iterable<DocTree> {
/**
* Gets a documentation tree path for a tree node within a compilation unit.
* @return null if the node is not found
*/
public static DocTreePath getPath(TreePath treePath, DocCommentTree doc, DocTree target) {
return getPath(new DocTreePath(treePath, doc), target);
}
/**
* Gets a documentation tree path for a tree node within a subtree identified by a DocTreePath object.
* @return null if the node is not found
*/
public static DocTreePath getPath(DocTreePath path, DocTree target) {
path.getClass();
target.getClass();
class Result extends Error {
static final long serialVersionUID = -5942088234594905625L;
DocTreePath path;
Result(DocTreePath path) {
this.path = path;
}
}
class PathFinder extends DocTreePathScanner<DocTreePath,DocTree> {
public DocTreePath scan(DocTree tree, DocTree target) {
if (tree == target) {
throw new Result(new DocTreePath(getCurrentPath(), target));
}
return super.scan(tree, target);
}
}
if (path.getLeaf() == target) {
return path;
}
try {
new PathFinder().scan(path, target);
} catch (Result result) {
return result.path;
}
return null;
}
/**
* Creates a DocTreePath for a root node.
*
* @param treePath the TreePath from which the root node was created.
* @param t the DocCommentTree to create the path for.
*/
public DocTreePath(TreePath treePath, DocCommentTree t) {
treePath.getClass();
t.getClass();
this.treePath = treePath;
this.docComment = t;
this.parent = null;
this.leaf = t;
}
/**
* Creates a DocTreePath for a child node.
*/
public DocTreePath(DocTreePath p, DocTree t) {
if (t.getKind() == DocTree.Kind.DOC_COMMENT) {
throw new IllegalArgumentException("Use DocTreePath(TreePath, DocCommentTree) to construct DocTreePath for a DocCommentTree.");
} else {
treePath = p.treePath;
docComment = p.docComment;
parent = p;
}
leaf = t;
}
/**
* Get the TreePath associated with this path.
* @return TreePath for this DocTreePath
*/
public TreePath getTreePath() {
return treePath;
}
/**
* Get the DocCommentTree associated with this path.
* @return DocCommentTree for this DocTreePath
*/
public DocCommentTree getDocComment() {
return docComment;
}
/**
* Get the leaf node for this path.
* @return DocTree for this DocTreePath
*/
public DocTree getLeaf() {
return leaf;
}
/**
* Get the path for the enclosing node, or null if there is no enclosing node.
* @return DocTreePath of parent
*/
public DocTreePath getParentPath() {
return parent;
}
public Iterator<DocTree> iterator() {
return new Iterator<DocTree>() {
public boolean hasNext() {
return next != null;
}
public DocTree next() {
DocTree t = next.leaf;
next = next.parent;
return t;
}
public void remove() {
throw new UnsupportedOperationException();
}
private DocTreePath next = DocTreePath.this;
};
}
private final TreePath treePath;
private final DocCommentTree docComment;
private final DocTree leaf;
private final DocTreePath parent;
}

View File

@ -0,0 +1,80 @@
/*
* Copyright (c) 2006, 2013, 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 com.sun.source.util;
import com.sun.source.doctree.DocTree;
/**
* A DocTreeVisitor that visits all the child tree nodes, and provides
* support for maintaining a path for the parent nodes.
* To visit nodes of a particular type, just override the
* corresponding visitorXYZ method.
* Inside your method, call super.visitXYZ to visit descendant
* nodes.
*
* @since 1.8
*/
@jdk.Supported
public class DocTreePathScanner<R, P> extends DocTreeScanner<R, P> {
/**
* Scan a tree from a position identified by a TreePath.
*/
public R scan(DocTreePath path, P p) {
this.path = path;
try {
return path.getLeaf().accept(this, p);
} finally {
this.path = null;
}
}
/**
* Scan a single node.
* The current path is updated for the duration of the scan.
*/
@Override
public R scan(DocTree tree, P p) {
if (tree == null)
return null;
DocTreePath prev = path;
path = new DocTreePath(path, tree);
try {
return tree.accept(this, p);
} finally {
path = prev;
}
}
/**
* Get the current path for the node, as built up by the currently
* active set of scan calls.
*/
public DocTreePath getCurrentPath() {
return path;
}
private DocTreePath path;
}

View File

@ -67,10 +67,10 @@ public abstract class DocTrees extends Trees {
public abstract DocCommentTree getDocCommentTree(TreePath path);
/**
* Gets the language model element referred to by a ReferenceTree that
* appears on the declaration identified by the given path.
* Gets the language model element referred to by the leaf node of the given
* {@link DocTreePath}, or null if unknown.
*/
public abstract Element getElement(TreePath path, ReferenceTree reference);
public abstract Element getElement(DocTreePath path);
public abstract DocSourcePositions getSourcePositions();

View File

@ -42,7 +42,6 @@ import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic.Kind;
@ -70,7 +69,8 @@ import com.sun.source.doctree.TextTree;
import com.sun.source.doctree.ThrowsTree;
import com.sun.source.doctree.ValueTree;
import com.sun.source.doctree.VersionTree;
import com.sun.source.util.DocTreeScanner;
import com.sun.source.util.DocTreePath;
import com.sun.source.util.DocTreePathScanner;
import com.sun.source.util.TreePath;
import com.sun.tools.doclint.HtmlTag.AttrKind;
import com.sun.tools.javac.tree.DocPretty;
@ -85,7 +85,7 @@ import static com.sun.tools.doclint.Messages.Group.*;
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
*/
public class Checker extends DocTreeScanner<Void, Void> {
public class Checker extends DocTreePathScanner<Void, Void> {
final Env env;
Set<Element> foundParams = new HashSet<Element>();
@ -152,7 +152,7 @@ public class Checker extends DocTreeScanner<Void, Void> {
foundInheritDoc = false;
foundReturn = false;
scan(tree, (Void) null);
scan(new DocTreePath(p, tree), null);
if (!isOverridingMethod) {
switch (env.currElement.getKind()) {
@ -620,47 +620,36 @@ public class Checker extends DocTreeScanner<Void, Void> {
}
@Override
@SuppressWarnings("fallthrough")
public Void visitParam(ParamTree tree, Void ignore) {
boolean typaram = tree.isTypeParameter();
IdentifierTree nameTree = tree.getName();
Element e = env.currElement;
switch (e.getKind()) {
case METHOD: case CONSTRUCTOR: {
ExecutableElement ee = (ExecutableElement) e;
checkParamDeclared(nameTree, typaram ? ee.getTypeParameters() : ee.getParameters());
break;
}
Element paramElement = nameTree != null ? env.trees.getElement(new DocTreePath(getCurrentPath(), nameTree)) : null;
case CLASS: case INTERFACE: {
TypeElement te = (TypeElement) e;
if (typaram) {
checkParamDeclared(nameTree, te.getTypeParameters());
} else {
env.messages.error(REFERENCE, tree, "dc.invalid.param");
if (paramElement == null) {
switch (env.currElement.getKind()) {
case CLASS: case INTERFACE: {
if (!typaram) {
env.messages.error(REFERENCE, tree, "dc.invalid.param");
break;
}
}
case METHOD: case CONSTRUCTOR: {
env.messages.error(REFERENCE, nameTree, "dc.param.name.not.found");
break;
}
break;
}
default:
env.messages.error(REFERENCE, tree, "dc.invalid.param");
break;
default:
env.messages.error(REFERENCE, tree, "dc.invalid.param");
break;
}
} else {
foundParams.add(paramElement);
}
warnIfEmpty(tree, tree.getDescription());
return super.visitParam(tree, ignore);
}
// where
private void checkParamDeclared(IdentifierTree nameTree, List<? extends Element> list) {
Name name = nameTree.getName();
boolean found = false;
for (Element e: list) {
if (name.equals(e.getSimpleName())) {
foundParams.add(e);
found = true;
}
}
if (!found)
env.messages.error(REFERENCE, nameTree, "dc.param.name.not.found");
}
private void checkParamsDocumented(List<? extends Element> list) {
if (foundInheritDoc)
@ -678,7 +667,7 @@ public class Checker extends DocTreeScanner<Void, Void> {
@Override
public Void visitReference(ReferenceTree tree, Void ignore) {
Element e = env.trees.getElement(env.currPath, tree);
Element e = env.trees.getElement(getCurrentPath());
if (e == null)
env.messages.error(REFERENCE, tree, "dc.ref.not.found");
return super.visitReference(tree, ignore);
@ -716,7 +705,7 @@ public class Checker extends DocTreeScanner<Void, Void> {
@Override
public Void visitThrows(ThrowsTree tree, Void ignore) {
ReferenceTree exName = tree.getExceptionName();
Element ex = env.trees.getElement(env.currPath, exName);
Element ex = env.trees.getElement(new DocTreePath(getCurrentPath(), exName));
if (ex == null) {
env.messages.error(REFERENCE, tree, "dc.ref.not.found");
} else if (ex.asType().getKind() == TypeKind.DECLARED

View File

@ -33,6 +33,7 @@ import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
@ -44,12 +45,12 @@ import javax.tools.JavaFileObject;
import com.sun.source.doctree.DocCommentTree;
import com.sun.source.doctree.DocTree;
import com.sun.source.doctree.ReferenceTree;
import com.sun.source.tree.CatchTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.Scope;
import com.sun.source.tree.Tree;
import com.sun.source.util.DocSourcePositions;
import com.sun.source.util.DocTreePath;
import com.sun.source.util.DocTreeScanner;
import com.sun.source.util.DocTrees;
import com.sun.source.util.JavacTask;
@ -314,7 +315,7 @@ public class JavacTrees extends DocTrees {
return TreePath.getPath(treeTopLevel.snd, treeTopLevel.fst);
}
public Element getElement(TreePath path) {
public Symbol getElement(TreePath path) {
JCTree tree = (JCTree) path.getLeaf();
Symbol sym = TreeInfo.symbolFor(tree);
if (sym == null) {
@ -343,11 +344,19 @@ public class JavacTrees extends DocTrees {
}
@Override
public Element getElement(TreePath path, ReferenceTree reference) {
if (!(reference instanceof DCReference))
return null;
DCReference ref = (DCReference) reference;
public Element getElement(DocTreePath path) {
DocTree forTree = path.getLeaf();
if (forTree instanceof DCReference)
return attributeDocReference(path.getTreePath(), ((DCReference) forTree));
if (forTree instanceof DCIdentifier) {
if (path.getParentPath().getLeaf() instanceof DCParam) {
return attributeParamIdentifier(path.getTreePath(), (DCParam) path.getParentPath().getLeaf());
}
}
return null;
}
private Symbol attributeDocReference(TreePath path, DCReference ref) {
Env<AttrContext> env = getAttrContext(path);
Log.DeferredDiagnosticHandler deferredDiagnosticHandler =
@ -427,6 +436,30 @@ public class JavacTrees extends DocTrees {
}
}
private Symbol attributeParamIdentifier(TreePath path, DCParam ptag) {
Symbol javadocSymbol = getElement(path);
if (javadocSymbol == null)
return null;
ElementKind kind = javadocSymbol.getKind();
List<? extends Symbol> params = List.nil();
if (kind == ElementKind.METHOD || kind == ElementKind.CONSTRUCTOR) {
MethodSymbol ee = (MethodSymbol) javadocSymbol;
params = ptag.isTypeParameter()
? ee.getTypeParameters()
: ee.getParameters();
} else if (kind.isClass() || kind.isInterface()) {
ClassSymbol te = (ClassSymbol) javadocSymbol;
params = te.getTypeParameters();
}
for (Symbol param : params) {
if (param.getSimpleName() == ptag.getName().getName()) {
return param;
}
}
return null;
}
/** @see com.sun.tools.javadoc.ClassDocImpl#findField */
private VarSymbol findField(ClassSymbol tsym, Name fieldName) {
return searchField(tsym, fieldName, new HashSet<ClassSymbol>());

View File

@ -0,0 +1,161 @@
/*
* Copyright (c) 2013, 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 8009724
* @summary adding DocTreePath and DocTreePathScanner
*/
import com.sun.source.doctree.DocCommentTree;
import com.sun.source.doctree.DocTree;
import com.sun.source.doctree.DocTree.Kind;
import com.sun.source.doctree.DocTreeVisitor;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.DocTreePath;
import com.sun.source.util.DocTreePathScanner;
import com.sun.source.util.DocTreeScanner;
import com.sun.source.util.DocTrees;
import com.sun.source.util.JavacTask;
import com.sun.source.util.TreePath;
import com.sun.source.util.TreePathScanner;
import com.sun.tools.javac.api.JavacTool;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import javax.lang.model.element.Name;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
public class DocTreePathScannerTest {
public static void main(String... args) throws Exception {
DocTreePathScannerTest t = new DocTreePathScannerTest();
t.run();
}
void run() throws Exception {
List<File> files = new ArrayList<File>();
File testSrc = new File(System.getProperty("test.src"));
for (File f: testSrc.listFiles()) {
if (f.isFile() && f.getName().endsWith(".java"))
files.add(f);
}
JavacTool javac = JavacTool.create();
StandardJavaFileManager fm = javac.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> fos = fm.getJavaFileObjectsFromFiles(files);
JavacTask t = javac.getTask(null, fm, null, null, null, fos);
DocTrees trees = DocTrees.instance(t);
Iterable<? extends CompilationUnitTree> units = t.parse();
DeclScanner ds = new DeclScanner(trees);
for (CompilationUnitTree unit: units) {
ds.scan(unit, null);
}
if (errors > 0)
throw new Exception(errors + " errors occurred");
}
void error(String msg) {
System.err.println("Error: " + msg);
errors++;
}
int errors;
class DeclScanner extends TreePathScanner<Void, Void> {
DocTrees trees;
DocTreePathScanner<Void,Void> cs;
DeclScanner(DocTrees trees) {
this.trees = trees;
cs = new CommentPathScanner();
}
@Override
public Void visitClass(ClassTree tree, Void ignore) {
super.visitClass(tree, ignore);
visitDecl(tree, tree.getSimpleName());
return null;
}
@Override
public Void visitMethod(MethodTree tree, Void ignore) {
super.visitMethod(tree, ignore);
visitDecl(tree, tree.getName());
return null;
}
@Override
public Void visitVariable(VariableTree tree, Void ignore) {
super.visitVariable(tree, ignore);
visitDecl(tree, tree.getName());
return null;
}
void visitDecl(Tree tree, Name name) {
TreePath path = getCurrentPath();
DocCommentTree dc = trees.getDocCommentTree(path);
if (dc != null)
cs.scan(new DocTreePath(path, dc), null);
}
}
class CommentPathScanner extends DocTreePathScanner<Void, Void> {
CommentPathScanner() {}
@Override
public Void scan(final DocTree tree, Void ignore) {
if (tree != null) {
DocTree previous = null;
for (DocTree current : getCurrentPath()) {
if (previous != null) {
final List<DocTree> children = new ArrayList<>();
current.accept(new DocTreeScanner<Void, Void>() {
@Override public Void scan(DocTree node, Void p) {
children.add(node);
return null;
}
}, null);
if (!children.contains(previous)) {
error("Invalid DocTreePath for: " + tree);
}
}
previous = current;
}
}
return super.scan(tree, ignore);
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2013, 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
@ -36,6 +36,8 @@ import com.sun.source.doctree.LinkTree;
import com.sun.source.doctree.ReferenceTree;
import com.sun.source.doctree.SeeTree;
import com.sun.source.doctree.TextTree;
import com.sun.source.util.DocTreePath;
import com.sun.source.util.DocTreePathScanner;
import com.sun.source.util.DocTreeScanner;
import com.sun.source.util.DocTrees;
import com.sun.source.util.TreePath;
@ -125,7 +127,7 @@ public class ReferenceTest extends AbstractProcessor {
return true;
}
class DocCommentScanner extends DocTreeScanner<Void, Void> {
class DocCommentScanner extends DocTreePathScanner<Void, Void> {
TreePath path;
DocCommentTree dc;
@ -135,7 +137,7 @@ public class ReferenceTest extends AbstractProcessor {
void scan() {
dc = trees.getDocCommentTree(path);
scan(dc, null);
scan(new DocTreePath(path, dc), null);
}
@Override
@ -158,7 +160,7 @@ public class ReferenceTest extends AbstractProcessor {
void checkReference(ReferenceTree tree, List<? extends DocTree> label) {
String sig = tree.getSignature();
Element found = trees.getElement(path, tree);
Element found = trees.getElement(new DocTreePath(getCurrentPath(), tree));
if (found == null) {
System.err.println(sig + " NOT FOUND");
} else {