--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.compiler/share/classes/jdk/internal/shellsupport/doc/JavadocHelper.java Wed Nov 02 07:38:37 2016 +0100
@@ -0,0 +1,661 @@
+/*
+ * Copyright (c) 2016, 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.internal.shellsupport.doc;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Set;
+import java.util.Stack;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+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.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.util.ElementFilter;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
+
+import com.sun.source.doctree.DocCommentTree;
+import com.sun.source.doctree.DocTree;
+import com.sun.source.doctree.InheritDocTree;
+import com.sun.source.doctree.ParamTree;
+import com.sun.source.doctree.ReturnTree;
+import com.sun.source.doctree.ThrowsTree;
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.util.DocTreePath;
+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.source.util.Trees;
+import com.sun.tools.javac.api.JavacTaskImpl;
+import com.sun.tools.javac.util.DefinedBy;
+import com.sun.tools.javac.util.DefinedBy.Api;
+import com.sun.tools.javac.util.Pair;
+
+/**Helper to find javadoc and resolve @inheritDoc.
+ */
+public abstract class JavadocHelper implements AutoCloseable {
+ private static final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+
+ /**Create the helper.
+ *
+ * @param mainTask JavacTask from which the further Elements originate
+ * @param sourceLocations paths where source files should be searched
+ * @return a JavadocHelper
+ */
+ public static JavadocHelper create(JavacTask mainTask, Collection<? extends Path> sourceLocations) {
+ StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null);
+ try {
+ fm.setLocationFromPaths(StandardLocation.SOURCE_PATH, sourceLocations);
+ return new OnDemandJavadocHelper(mainTask, fm);
+ } catch (IOException ex) {
+ try {
+ fm.close();
+ } catch (IOException closeEx) {
+ }
+ return new JavadocHelper() {
+ @Override
+ public String getResolvedDocComment(Element forElement) throws IOException {
+ return null;
+ }
+ @Override
+ public Element getSourceElement(Element forElement) throws IOException {
+ return forElement;
+ }
+ @Override
+ public void close() throws IOException {}
+ };
+ }
+ }
+
+ /**Returns javadoc for the given element, if it can be found, or null otherwise. The javadoc
+ * will have @inheritDoc resolved.
+ *
+ * @param forElement element for which the javadoc should be searched
+ * @return javadoc if found, null otherwise
+ * @throws IOException if something goes wrong in the search
+ */
+ public abstract String getResolvedDocComment(Element forElement) throws IOException;
+
+ /**Returns an element representing the same given program element, but the returned element will
+ * be resolved from source, if it can be found. Returns the original element if the source for
+ * the given element cannot be found.
+ *
+ * @param forElement element for which the source element should be searched
+ * @return source element if found, the original element otherwise
+ * @throws IOException if something goes wrong in the search
+ */
+ public abstract Element getSourceElement(Element forElement) throws IOException;
+
+ /**Closes the helper.
+ *
+ * @throws IOException if something foes wrong during the close
+ */
+ @Override
+ public abstract void close() throws IOException;
+
+ private static final class OnDemandJavadocHelper extends JavadocHelper {
+ private final JavacTask mainTask;
+ private final JavaFileManager baseFileManager;
+ private final StandardJavaFileManager fm;
+ private final Map<String, Pair<JavacTask, TreePath>> signature2Source = new HashMap<>();
+
+ private OnDemandJavadocHelper(JavacTask mainTask, StandardJavaFileManager fm) {
+ this.mainTask = mainTask;
+ this.baseFileManager = ((JavacTaskImpl) mainTask).getContext().get(JavaFileManager.class);
+ this.fm = fm;
+ }
+
+ @Override
+ public String getResolvedDocComment(Element forElement) throws IOException {
+ Pair<JavacTask, TreePath> sourceElement = getSourceElement(mainTask, forElement);
+
+ if (sourceElement == null)
+ return null;
+
+ return getResolvedDocComment(sourceElement.fst, sourceElement.snd);
+ }
+
+ @Override
+ public Element getSourceElement(Element forElement) throws IOException {
+ Pair<JavacTask, TreePath> sourceElement = getSourceElement(mainTask, forElement);
+
+ if (sourceElement == null)
+ return forElement;
+
+ Element result = Trees.instance(sourceElement.fst).getElement(sourceElement.snd);
+
+ if (result == null)
+ return forElement;
+
+ return result;
+ }
+
+ private String getResolvedDocComment(JavacTask task, TreePath el) throws IOException {
+ DocTrees trees = DocTrees.instance(task);
+ Element element = trees.getElement(el);
+ String docComment = trees.getDocComment(el);
+
+ if (docComment == null && element.getKind() == ElementKind.METHOD) {
+ ExecutableElement executableElement = (ExecutableElement) element;
+ Iterable<Element> superTypes =
+ () -> superTypeForInheritDoc(task, element.getEnclosingElement()).iterator();
+ for (Element sup : superTypes) {
+ for (ExecutableElement supMethod : ElementFilter.methodsIn(sup.getEnclosedElements())) {
+ TypeElement clazz = (TypeElement) executableElement.getEnclosingElement();
+ if (task.getElements().overrides(executableElement, supMethod, clazz)) {
+ Pair<JavacTask, TreePath> source = getSourceElement(task, supMethod);
+
+ if (source != null) {
+ String overriddenComment = getResolvedDocComment(source.fst, source.snd);
+
+ if (overriddenComment != null) {
+ return overriddenComment;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ DocCommentTree docCommentTree = parseDocComment(task, docComment);
+ IOException[] exception = new IOException[1];
+ Map<int[], String> replace = new TreeMap<>((span1, span2) -> span2[0] - span1[0]);
+
+ new DocTreeScanner<Void, Void>() {
+ private Stack<DocTree> interestingParent = new Stack<>();
+ private DocCommentTree dcTree;
+ private JavacTask inheritedJavacTask;
+ private TreePath inheritedTreePath;
+ private String inherited;
+ private Map<DocTree, String> syntheticTrees = new IdentityHashMap<>();
+ private long lastPos = 0;
+ @Override @DefinedBy(Api.COMPILER_TREE)
+ public Void visitDocComment(DocCommentTree node, Void p) {
+ dcTree = node;
+ interestingParent.push(node);
+ try {
+ scan(node.getFirstSentence(), p);
+ scan(node.getBody(), p);
+ List<DocTree> augmentedBlockTags = new ArrayList<>(node.getBlockTags());
+ if (element.getKind() == ElementKind.METHOD) {
+ ExecutableElement executableElement = (ExecutableElement) element;
+ List<String> parameters =
+ executableElement.getParameters()
+ .stream()
+ .map(param -> param.getSimpleName().toString())
+ .collect(Collectors.toList());
+ List<String> throwsList =
+ executableElement.getThrownTypes()
+ .stream()
+ .map(exc -> exc.toString())
+ .collect(Collectors.toList());
+ Set<String> missingParams = new HashSet<>(parameters);
+ Set<String> missingThrows = new HashSet<>(throwsList);
+ boolean hasReturn = false;
+
+ for (DocTree dt : augmentedBlockTags) {
+ switch (dt.getKind()) {
+ case PARAM:
+ missingParams.remove(((ParamTree) dt).getName().getName().toString());
+ break;
+ case THROWS:
+ missingThrows.remove(getThrownException(task, el, docCommentTree, (ThrowsTree) dt));
+ break;
+ case RETURN:
+ hasReturn = true;
+ break;
+ }
+ }
+
+ for (String missingParam : missingParams) {
+ DocTree syntheticTag = parseBlockTag(task, "@param " + missingParam + " {@inheritDoc}");
+ syntheticTrees.put(syntheticTag, "@param " + missingParam + " ");
+ insertTag(augmentedBlockTags, syntheticTag, parameters, throwsList);
+ }
+
+ for (String missingThrow : missingThrows) {
+ DocTree syntheticTag = parseBlockTag(task, "@throws " + missingThrow + " {@inheritDoc}");
+ syntheticTrees.put(syntheticTag, "@throws " + missingThrow + " ");
+ insertTag(augmentedBlockTags, syntheticTag, parameters, throwsList);
+ }
+
+ if (!hasReturn) {
+ DocTree syntheticTag = parseBlockTag(task, "@return {@inheritDoc}");
+ syntheticTrees.put(syntheticTag, "@return ");
+ insertTag(augmentedBlockTags, syntheticTag, parameters, throwsList);
+ }
+ }
+ scan(augmentedBlockTags, p);
+ return null;
+ } finally {
+ interestingParent.pop();
+ }
+ }
+ @Override @DefinedBy(Api.COMPILER_TREE)
+ public Void visitParam(ParamTree node, Void p) {
+ interestingParent.push(node);
+ try {
+ return super.visitParam(node, p);
+ } finally {
+ interestingParent.pop();
+ }
+ }
+ @Override @DefinedBy(Api.COMPILER_TREE)
+ public Void visitThrows(ThrowsTree node, Void p) {
+ interestingParent.push(node);
+ try {
+ return super.visitThrows(node, p);
+ } finally {
+ interestingParent.pop();
+ }
+ }
+ @Override @DefinedBy(Api.COMPILER_TREE)
+ public Void visitReturn(ReturnTree node, Void p) {
+ interestingParent.push(node);
+ try {
+ return super.visitReturn(node, p);
+ } finally {
+ interestingParent.pop();
+ }
+ }
+ @Override @DefinedBy(Api.COMPILER_TREE)
+ public Void visitInheritDoc(InheritDocTree node, Void p) {
+ if (inherited == null) {
+ try {
+ if (element.getKind() == ElementKind.METHOD) {
+ ExecutableElement executableElement = (ExecutableElement) element;
+ Iterable<Element> superTypes = () -> superTypeForInheritDoc(task, element.getEnclosingElement()).iterator();
+ OUTER: for (Element sup : superTypes) {
+ for (ExecutableElement supMethod : ElementFilter.methodsIn(sup.getEnclosedElements())) {
+ if (task.getElements().overrides(executableElement, supMethod, (TypeElement) executableElement.getEnclosingElement())) {
+ Pair<JavacTask, TreePath> source = getSourceElement(task, supMethod);
+
+ if (source != null) {
+ String overriddenComment = getResolvedDocComment(source.fst, source.snd);
+
+ if (overriddenComment != null) {
+ inheritedJavacTask = source.fst;
+ inheritedTreePath = source.snd;
+ inherited = overriddenComment;
+ break OUTER;
+ }
+ }
+ }
+ }
+ }
+ }
+ } catch (IOException ex) {
+ exception[0] = ex;
+ return null;
+ }
+ }
+ if (inherited == null) {
+ return null;
+ }
+ DocCommentTree inheritedDocTree = parseDocComment(inheritedJavacTask, inherited);
+ List<List<? extends DocTree>> inheritedText = new ArrayList<>();
+ DocTree parent = interestingParent.peek();
+ switch (parent.getKind()) {
+ case DOC_COMMENT:
+ inheritedText.add(inheritedDocTree.getFullBody());
+ break;
+ case PARAM:
+ String paramName = ((ParamTree) parent).getName().getName().toString();
+ new DocTreeScanner<Void, Void>() {
+ @Override @DefinedBy(Api.COMPILER_TREE)
+ public Void visitParam(ParamTree node, Void p) {
+ if (node.getName().getName().contentEquals(paramName)) {
+ inheritedText.add(node.getDescription());
+ }
+ return super.visitParam(node, p);
+ }
+ }.scan(inheritedDocTree, null);
+ break;
+ case THROWS:
+ String thrownName = getThrownException(task, el, docCommentTree, (ThrowsTree) parent);
+ new DocTreeScanner<Void, Void>() {
+ @Override @DefinedBy(Api.COMPILER_TREE)
+ public Void visitThrows(ThrowsTree node, Void p) {
+ if (Objects.equals(getThrownException(inheritedJavacTask, inheritedTreePath, inheritedDocTree, node), thrownName)) {
+ inheritedText.add(node.getDescription());
+ }
+ return super.visitThrows(node, p);
+ }
+ }.scan(inheritedDocTree, null);
+ break;
+ case RETURN:
+ new DocTreeScanner<Void, Void>() {
+ @Override @DefinedBy(Api.COMPILER_TREE)
+ public Void visitReturn(ReturnTree node, Void p) {
+ inheritedText.add(node.getDescription());
+ return super.visitReturn(node, p);
+ }
+ }.scan(inheritedDocTree, null);
+ break;
+ }
+ if (!inheritedText.isEmpty()) {
+ long offset = trees.getSourcePositions().getStartPosition(null, inheritedDocTree, inheritedDocTree);
+ long start = Long.MAX_VALUE;
+ long end = Long.MIN_VALUE;
+
+ for (DocTree t : inheritedText.get(0)) {
+ start = Math.min(start, trees.getSourcePositions().getStartPosition(null, inheritedDocTree, t) - offset);
+ end = Math.max(end, trees.getSourcePositions().getEndPosition(null, inheritedDocTree, t) - offset);
+ }
+ String text = inherited.substring((int) start, (int) end);
+
+ if (syntheticTrees.containsKey(parent)) {
+ replace.put(new int[] {(int) lastPos + 1, (int) lastPos}, "\n" + syntheticTrees.get(parent) + text);
+ } else {
+ long inheritedStart = trees.getSourcePositions().getStartPosition(null, dcTree, node);
+ long inheritedEnd = trees.getSourcePositions().getEndPosition(null, dcTree, node);
+
+ replace.put(new int[] {(int) inheritedStart, (int) inheritedEnd}, text);
+ }
+ }
+ return super.visitInheritDoc(node, p);
+ }
+ private boolean inSynthetic;
+ @Override @DefinedBy(Api.COMPILER_TREE)
+ public Void scan(DocTree tree, Void p) {
+ if (exception[0] != null) {
+ return null;
+ }
+ boolean prevInSynthetic = inSynthetic;
+ try {
+ inSynthetic |= syntheticTrees.containsKey(tree);
+ return super.scan(tree, p);
+ } finally {
+ if (!inSynthetic) {
+ lastPos = trees.getSourcePositions().getEndPosition(null, dcTree, tree);
+ }
+ inSynthetic = prevInSynthetic;
+ }
+ }
+
+ private void insertTag(List<DocTree> tags, DocTree toInsert, List<String> parameters, List<String> throwsTypes) {
+ Comparator<DocTree> comp = (tag1, tag2) -> {
+ if (tag1.getKind() == tag2.getKind()) {
+ switch (toInsert.getKind()) {
+ case PARAM: {
+ ParamTree p1 = (ParamTree) tag1;
+ ParamTree p2 = (ParamTree) tag2;
+ int i1 = parameters.indexOf(p1.getName().getName().toString());
+ int i2 = parameters.indexOf(p2.getName().getName().toString());
+
+ return i1 - i2;
+ }
+ case THROWS: {
+ ThrowsTree t1 = (ThrowsTree) tag1;
+ ThrowsTree t2 = (ThrowsTree) tag2;
+ int i1 = throwsTypes.indexOf(getThrownException(task, el, docCommentTree, t1));
+ int i2 = throwsTypes.indexOf(getThrownException(task, el, docCommentTree, t2));
+
+ return i1 - i2;
+ }
+ }
+ }
+
+ int i1 = tagOrder.indexOf(tag1.getKind());
+ int i2 = tagOrder.indexOf(tag2.getKind());
+
+ return i1 - i2;
+ };
+
+ for (int i = 0; i < tags.size(); i++) {
+ if (comp.compare(tags.get(i), toInsert) >= 0) {
+ tags.add(i, toInsert);
+ return ;
+ }
+ }
+ tags.add(toInsert);
+ }
+
+ private final List<DocTree.Kind> tagOrder = Arrays.asList(DocTree.Kind.PARAM, DocTree.Kind.THROWS, DocTree.Kind.RETURN);
+ }.scan(docCommentTree, null);
+
+ if (replace.isEmpty())
+ return docComment;
+
+ StringBuilder replacedInheritDoc = new StringBuilder(docComment);
+ int offset = (int) trees.getSourcePositions().getStartPosition(null, docCommentTree, docCommentTree);
+
+ for (Entry<int[], String> e : replace.entrySet()) {
+ replacedInheritDoc.delete(e.getKey()[0] - offset, e.getKey()[1] - offset + 1);
+ replacedInheritDoc.insert(e.getKey()[0] - offset, e.getValue());
+ }
+
+ return replacedInheritDoc.toString();
+ }
+
+ private Stream<Element> superTypeForInheritDoc(JavacTask task, Element type) {
+ TypeElement clazz = (TypeElement) type;
+ Stream<Element> result = interfaces(clazz);
+ result = Stream.concat(result, interfaces(clazz).flatMap(el -> superTypeForInheritDoc(task, el)));
+
+ if (clazz.getSuperclass().getKind() == TypeKind.DECLARED) {
+ Element superClass = ((DeclaredType) clazz.getSuperclass()).asElement();
+ result = Stream.concat(result, Stream.of(superClass));
+ result = Stream.concat(result, superTypeForInheritDoc(task, superClass));
+ }
+
+ return result;
+ }
+ //where:
+ private Stream<Element> interfaces(TypeElement clazz) {
+ return clazz.getInterfaces()
+ .stream()
+ .filter(tm -> tm.getKind() == TypeKind.DECLARED)
+ .map(tm -> ((DeclaredType) tm).asElement());
+ }
+
+ private DocTree parseBlockTag(JavacTask task, String blockTag) {
+ DocCommentTree dc = parseDocComment(task, blockTag);
+
+ return dc.getBlockTags().get(0);
+ }
+
+ private DocCommentTree parseDocComment(JavacTask task, String javadoc) {
+ DocTrees trees = DocTrees.instance(task);
+ try {
+ return trees.getDocCommentTree(new SimpleJavaFileObject(new URI("mem://doc.html"), javax.tools.JavaFileObject.Kind.HTML) {
+ @Override @DefinedBy(Api.COMPILER)
+ public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+ return "<body>" + javadoc + "</body>";
+ }
+ });
+ } catch (URISyntaxException ex) {
+ return null;
+ }
+ }
+
+ private String getThrownException(JavacTask task, TreePath rootOn, DocCommentTree comment, ThrowsTree tt) {
+ DocTrees trees = DocTrees.instance(task);
+ Element exc = trees.getElement(new DocTreePath(new DocTreePath(rootOn, comment), tt.getExceptionName()));
+ return exc != null ? exc.toString() : null;
+ }
+
+ private Pair<JavacTask, TreePath> getSourceElement(JavacTask origin, Element el) throws IOException {
+ String handle = elementSignature(el);
+ Pair<JavacTask, TreePath> cached = signature2Source.get(handle);
+
+ if (cached != null) {
+ return cached.fst != null ? cached : null;
+ }
+
+ TypeElement type = topLevelType(el);
+
+ if (type == null)
+ return null;
+
+ String binaryName = origin.getElements().getBinaryName(type).toString();
+ Pair<JavacTask, CompilationUnitTree> source = findSource(binaryName);
+
+ if (source == null)
+ return null;
+
+ fillElementCache(source.fst, source.snd);
+
+ cached = signature2Source.get(handle);
+
+ if (cached != null) {
+ return cached;
+ } else {
+ signature2Source.put(handle, Pair.of(null, null));
+ return null;
+ }
+ }
+ //where:
+ private String elementSignature(Element el) {
+ switch (el.getKind()) {
+ case ANNOTATION_TYPE: case CLASS: case ENUM: case INTERFACE:
+ return ((TypeElement) el).getQualifiedName().toString();
+ case FIELD:
+ return elementSignature(el.getEnclosingElement()) + "." + el.getSimpleName() + ":" + el.asType();
+ case ENUM_CONSTANT:
+ return elementSignature(el.getEnclosingElement()) + "." + el.getSimpleName();
+ case EXCEPTION_PARAMETER: case LOCAL_VARIABLE: case PARAMETER: case RESOURCE_VARIABLE:
+ return el.getSimpleName() + ":" + el.asType();
+ case CONSTRUCTOR: case METHOD:
+ StringBuilder header = new StringBuilder();
+ header.append(elementSignature(el.getEnclosingElement()));
+ if (el.getKind() == ElementKind.METHOD) {
+ header.append(".");
+ header.append(el.getSimpleName());
+ }
+ header.append("(");
+ String sep = "";
+ ExecutableElement method = (ExecutableElement) el;
+ for (Iterator<? extends VariableElement> i = method.getParameters().iterator(); i.hasNext();) {
+ VariableElement p = i.next();
+ header.append(sep);
+ header.append(p.asType());
+ sep = ", ";
+ }
+ header.append(")");
+ return header.toString();
+ default:
+ return el.toString();
+ }
+ }
+
+ private TypeElement topLevelType(Element el) {
+ if (el.getKind() == ElementKind.PACKAGE)
+ return null;
+
+ while (el != null && el.getEnclosingElement().getKind() != ElementKind.PACKAGE) {
+ el = el.getEnclosingElement();
+ }
+
+ return el != null && (el.getKind().isClass() || el.getKind().isInterface()) ? (TypeElement) el : null;
+ }
+
+ private void fillElementCache(JavacTask task, CompilationUnitTree cut) throws IOException {
+ Trees trees = Trees.instance(task);
+
+ new TreePathScanner<Void, Void>() {
+ @Override @DefinedBy(Api.COMPILER_TREE)
+ public Void visitMethod(MethodTree node, Void p) {
+ handleDeclaration();
+ return null;
+ }
+
+ @Override @DefinedBy(Api.COMPILER_TREE)
+ public Void visitClass(ClassTree node, Void p) {
+ handleDeclaration();
+ return super.visitClass(node, p);
+ }
+
+ @Override @DefinedBy(Api.COMPILER_TREE)
+ public Void visitVariable(VariableTree node, Void p) {
+ handleDeclaration();
+ return super.visitVariable(node, p);
+ }
+
+ private void handleDeclaration() {
+ Element currentElement = trees.getElement(getCurrentPath());
+
+ if (currentElement != null) {
+ signature2Source.put(elementSignature(currentElement), Pair.of(task, getCurrentPath()));
+ }
+ }
+ }.scan(cut, null);
+ }
+
+ private Pair<JavacTask, CompilationUnitTree> findSource(String binaryName) throws IOException {
+ JavaFileObject jfo = fm.getJavaFileForInput(StandardLocation.SOURCE_PATH,
+ binaryName,
+ JavaFileObject.Kind.SOURCE);
+
+ if (jfo == null)
+ return null;
+
+ List<JavaFileObject> jfos = Arrays.asList(jfo);
+ JavacTaskImpl task = (JavacTaskImpl) compiler.getTask(null, baseFileManager, d -> {}, null, null, jfos);
+ Iterable<? extends CompilationUnitTree> cuts = task.parse();
+
+ task.enter();
+
+ return Pair.of(task, cuts.iterator().next());
+ }
+
+ @Override
+ public void close() throws IOException {
+ fm.close();
+ }
+ }
+
+}