JavadocHelper.java revision 3738:6ef8a1453577
1/* 2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25package jdk.internal.shellsupport.doc; 26 27import java.io.IOException; 28import java.net.URI; 29import java.net.URISyntaxException; 30import java.nio.file.Path; 31import java.util.ArrayList; 32import java.util.Arrays; 33import java.util.Collection; 34import java.util.Comparator; 35import java.util.HashMap; 36import java.util.HashSet; 37import java.util.IdentityHashMap; 38import java.util.Iterator; 39import java.util.List; 40import java.util.Map; 41import java.util.Map.Entry; 42import java.util.Objects; 43import java.util.Set; 44import java.util.Stack; 45import java.util.TreeMap; 46import java.util.stream.Collectors; 47import java.util.stream.Stream; 48 49import javax.lang.model.element.Element; 50import javax.lang.model.element.ElementKind; 51import javax.lang.model.element.ExecutableElement; 52import javax.lang.model.element.TypeElement; 53import javax.lang.model.element.VariableElement; 54import javax.lang.model.type.DeclaredType; 55import javax.lang.model.type.TypeKind; 56import javax.lang.model.util.ElementFilter; 57import javax.tools.JavaCompiler; 58import javax.tools.JavaFileManager; 59import javax.tools.JavaFileObject; 60import javax.tools.SimpleJavaFileObject; 61import javax.tools.StandardJavaFileManager; 62import javax.tools.StandardLocation; 63import javax.tools.ToolProvider; 64 65import com.sun.source.doctree.DocCommentTree; 66import com.sun.source.doctree.DocTree; 67import com.sun.source.doctree.InheritDocTree; 68import com.sun.source.doctree.ParamTree; 69import com.sun.source.doctree.ReturnTree; 70import com.sun.source.doctree.ThrowsTree; 71import com.sun.source.tree.ClassTree; 72import com.sun.source.tree.CompilationUnitTree; 73import com.sun.source.tree.MethodTree; 74import com.sun.source.tree.VariableTree; 75import com.sun.source.util.DocTreePath; 76import com.sun.source.util.DocTreeScanner; 77import com.sun.source.util.DocTrees; 78import com.sun.source.util.JavacTask; 79import com.sun.source.util.TreePath; 80import com.sun.source.util.TreePathScanner; 81import com.sun.source.util.Trees; 82import com.sun.tools.javac.api.JavacTaskImpl; 83import com.sun.tools.javac.util.DefinedBy; 84import com.sun.tools.javac.util.DefinedBy.Api; 85import com.sun.tools.javac.util.Pair; 86 87/**Helper to find javadoc and resolve @inheritDoc. 88 */ 89public abstract class JavadocHelper implements AutoCloseable { 90 private static final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 91 92 /**Create the helper. 93 * 94 * @param mainTask JavacTask from which the further Elements originate 95 * @param sourceLocations paths where source files should be searched 96 * @return a JavadocHelper 97 */ 98 public static JavadocHelper create(JavacTask mainTask, Collection<? extends Path> sourceLocations) { 99 StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null); 100 try { 101 fm.setLocationFromPaths(StandardLocation.SOURCE_PATH, sourceLocations); 102 return new OnDemandJavadocHelper(mainTask, fm); 103 } catch (IOException ex) { 104 try { 105 fm.close(); 106 } catch (IOException closeEx) { 107 } 108 return new JavadocHelper() { 109 @Override 110 public String getResolvedDocComment(Element forElement) throws IOException { 111 return null; 112 } 113 @Override 114 public Element getSourceElement(Element forElement) throws IOException { 115 return forElement; 116 } 117 @Override 118 public void close() throws IOException {} 119 }; 120 } 121 } 122 123 /**Returns javadoc for the given element, if it can be found, or null otherwise. The javadoc 124 * will have @inheritDoc resolved. 125 * 126 * @param forElement element for which the javadoc should be searched 127 * @return javadoc if found, null otherwise 128 * @throws IOException if something goes wrong in the search 129 */ 130 public abstract String getResolvedDocComment(Element forElement) throws IOException; 131 132 /**Returns an element representing the same given program element, but the returned element will 133 * be resolved from source, if it can be found. Returns the original element if the source for 134 * the given element cannot be found. 135 * 136 * @param forElement element for which the source element should be searched 137 * @return source element if found, the original element otherwise 138 * @throws IOException if something goes wrong in the search 139 */ 140 public abstract Element getSourceElement(Element forElement) throws IOException; 141 142 /**Closes the helper. 143 * 144 * @throws IOException if something foes wrong during the close 145 */ 146 @Override 147 public abstract void close() throws IOException; 148 149 private static final class OnDemandJavadocHelper extends JavadocHelper { 150 private final JavacTask mainTask; 151 private final JavaFileManager baseFileManager; 152 private final StandardJavaFileManager fm; 153 private final Map<String, Pair<JavacTask, TreePath>> signature2Source = new HashMap<>(); 154 155 private OnDemandJavadocHelper(JavacTask mainTask, StandardJavaFileManager fm) { 156 this.mainTask = mainTask; 157 this.baseFileManager = ((JavacTaskImpl) mainTask).getContext().get(JavaFileManager.class); 158 this.fm = fm; 159 } 160 161 @Override 162 public String getResolvedDocComment(Element forElement) throws IOException { 163 Pair<JavacTask, TreePath> sourceElement = getSourceElement(mainTask, forElement); 164 165 if (sourceElement == null) 166 return null; 167 168 return getResolvedDocComment(sourceElement.fst, sourceElement.snd); 169 } 170 171 @Override 172 public Element getSourceElement(Element forElement) throws IOException { 173 Pair<JavacTask, TreePath> sourceElement = getSourceElement(mainTask, forElement); 174 175 if (sourceElement == null) 176 return forElement; 177 178 Element result = Trees.instance(sourceElement.fst).getElement(sourceElement.snd); 179 180 if (result == null) 181 return forElement; 182 183 return result; 184 } 185 186 private String getResolvedDocComment(JavacTask task, TreePath el) throws IOException { 187 DocTrees trees = DocTrees.instance(task); 188 Element element = trees.getElement(el); 189 String docComment = trees.getDocComment(el); 190 191 if (docComment == null && element.getKind() == ElementKind.METHOD) { 192 ExecutableElement executableElement = (ExecutableElement) element; 193 Iterable<Element> superTypes = 194 () -> superTypeForInheritDoc(task, element.getEnclosingElement()).iterator(); 195 for (Element sup : superTypes) { 196 for (ExecutableElement supMethod : ElementFilter.methodsIn(sup.getEnclosedElements())) { 197 TypeElement clazz = (TypeElement) executableElement.getEnclosingElement(); 198 if (task.getElements().overrides(executableElement, supMethod, clazz)) { 199 Pair<JavacTask, TreePath> source = getSourceElement(task, supMethod); 200 201 if (source != null) { 202 String overriddenComment = getResolvedDocComment(source.fst, source.snd); 203 204 if (overriddenComment != null) { 205 return overriddenComment; 206 } 207 } 208 } 209 } 210 } 211 } 212 213 DocCommentTree docCommentTree = parseDocComment(task, docComment); 214 IOException[] exception = new IOException[1]; 215 Map<int[], String> replace = new TreeMap<>((span1, span2) -> span2[0] - span1[0]); 216 217 new DocTreeScanner<Void, Void>() { 218 private Stack<DocTree> interestingParent = new Stack<>(); 219 private DocCommentTree dcTree; 220 private JavacTask inheritedJavacTask; 221 private TreePath inheritedTreePath; 222 private String inherited; 223 private Map<DocTree, String> syntheticTrees = new IdentityHashMap<>(); 224 private long lastPos = 0; 225 @Override @DefinedBy(Api.COMPILER_TREE) 226 public Void visitDocComment(DocCommentTree node, Void p) { 227 dcTree = node; 228 interestingParent.push(node); 229 try { 230 scan(node.getFirstSentence(), p); 231 scan(node.getBody(), p); 232 List<DocTree> augmentedBlockTags = new ArrayList<>(node.getBlockTags()); 233 if (element.getKind() == ElementKind.METHOD) { 234 ExecutableElement executableElement = (ExecutableElement) element; 235 List<String> parameters = 236 executableElement.getParameters() 237 .stream() 238 .map(param -> param.getSimpleName().toString()) 239 .collect(Collectors.toList()); 240 List<String> throwsList = 241 executableElement.getThrownTypes() 242 .stream() 243 .map(exc -> exc.toString()) 244 .collect(Collectors.toList()); 245 Set<String> missingParams = new HashSet<>(parameters); 246 Set<String> missingThrows = new HashSet<>(throwsList); 247 boolean hasReturn = false; 248 249 for (DocTree dt : augmentedBlockTags) { 250 switch (dt.getKind()) { 251 case PARAM: 252 missingParams.remove(((ParamTree) dt).getName().getName().toString()); 253 break; 254 case THROWS: 255 missingThrows.remove(getThrownException(task, el, docCommentTree, (ThrowsTree) dt)); 256 break; 257 case RETURN: 258 hasReturn = true; 259 break; 260 } 261 } 262 263 for (String missingParam : missingParams) { 264 DocTree syntheticTag = parseBlockTag(task, "@param " + missingParam + " {@inheritDoc}"); 265 syntheticTrees.put(syntheticTag, "@param " + missingParam + " "); 266 insertTag(augmentedBlockTags, syntheticTag, parameters, throwsList); 267 } 268 269 for (String missingThrow : missingThrows) { 270 DocTree syntheticTag = parseBlockTag(task, "@throws " + missingThrow + " {@inheritDoc}"); 271 syntheticTrees.put(syntheticTag, "@throws " + missingThrow + " "); 272 insertTag(augmentedBlockTags, syntheticTag, parameters, throwsList); 273 } 274 275 if (!hasReturn) { 276 DocTree syntheticTag = parseBlockTag(task, "@return {@inheritDoc}"); 277 syntheticTrees.put(syntheticTag, "@return "); 278 insertTag(augmentedBlockTags, syntheticTag, parameters, throwsList); 279 } 280 } 281 scan(augmentedBlockTags, p); 282 return null; 283 } finally { 284 interestingParent.pop(); 285 } 286 } 287 @Override @DefinedBy(Api.COMPILER_TREE) 288 public Void visitParam(ParamTree node, Void p) { 289 interestingParent.push(node); 290 try { 291 return super.visitParam(node, p); 292 } finally { 293 interestingParent.pop(); 294 } 295 } 296 @Override @DefinedBy(Api.COMPILER_TREE) 297 public Void visitThrows(ThrowsTree node, Void p) { 298 interestingParent.push(node); 299 try { 300 return super.visitThrows(node, p); 301 } finally { 302 interestingParent.pop(); 303 } 304 } 305 @Override @DefinedBy(Api.COMPILER_TREE) 306 public Void visitReturn(ReturnTree node, Void p) { 307 interestingParent.push(node); 308 try { 309 return super.visitReturn(node, p); 310 } finally { 311 interestingParent.pop(); 312 } 313 } 314 @Override @DefinedBy(Api.COMPILER_TREE) 315 public Void visitInheritDoc(InheritDocTree node, Void p) { 316 if (inherited == null) { 317 try { 318 if (element.getKind() == ElementKind.METHOD) { 319 ExecutableElement executableElement = (ExecutableElement) element; 320 Iterable<Element> superTypes = () -> superTypeForInheritDoc(task, element.getEnclosingElement()).iterator(); 321 OUTER: for (Element sup : superTypes) { 322 for (ExecutableElement supMethod : ElementFilter.methodsIn(sup.getEnclosedElements())) { 323 if (task.getElements().overrides(executableElement, supMethod, (TypeElement) executableElement.getEnclosingElement())) { 324 Pair<JavacTask, TreePath> source = getSourceElement(task, supMethod); 325 326 if (source != null) { 327 String overriddenComment = getResolvedDocComment(source.fst, source.snd); 328 329 if (overriddenComment != null) { 330 inheritedJavacTask = source.fst; 331 inheritedTreePath = source.snd; 332 inherited = overriddenComment; 333 break OUTER; 334 } 335 } 336 } 337 } 338 } 339 } 340 } catch (IOException ex) { 341 exception[0] = ex; 342 return null; 343 } 344 } 345 if (inherited == null) { 346 return null; 347 } 348 DocCommentTree inheritedDocTree = parseDocComment(inheritedJavacTask, inherited); 349 List<List<? extends DocTree>> inheritedText = new ArrayList<>(); 350 DocTree parent = interestingParent.peek(); 351 switch (parent.getKind()) { 352 case DOC_COMMENT: 353 inheritedText.add(inheritedDocTree.getFullBody()); 354 break; 355 case PARAM: 356 String paramName = ((ParamTree) parent).getName().getName().toString(); 357 new DocTreeScanner<Void, Void>() { 358 @Override @DefinedBy(Api.COMPILER_TREE) 359 public Void visitParam(ParamTree node, Void p) { 360 if (node.getName().getName().contentEquals(paramName)) { 361 inheritedText.add(node.getDescription()); 362 } 363 return super.visitParam(node, p); 364 } 365 }.scan(inheritedDocTree, null); 366 break; 367 case THROWS: 368 String thrownName = getThrownException(task, el, docCommentTree, (ThrowsTree) parent); 369 new DocTreeScanner<Void, Void>() { 370 @Override @DefinedBy(Api.COMPILER_TREE) 371 public Void visitThrows(ThrowsTree node, Void p) { 372 if (Objects.equals(getThrownException(inheritedJavacTask, inheritedTreePath, inheritedDocTree, node), thrownName)) { 373 inheritedText.add(node.getDescription()); 374 } 375 return super.visitThrows(node, p); 376 } 377 }.scan(inheritedDocTree, null); 378 break; 379 case RETURN: 380 new DocTreeScanner<Void, Void>() { 381 @Override @DefinedBy(Api.COMPILER_TREE) 382 public Void visitReturn(ReturnTree node, Void p) { 383 inheritedText.add(node.getDescription()); 384 return super.visitReturn(node, p); 385 } 386 }.scan(inheritedDocTree, null); 387 break; 388 } 389 if (!inheritedText.isEmpty()) { 390 long offset = trees.getSourcePositions().getStartPosition(null, inheritedDocTree, inheritedDocTree); 391 long start = Long.MAX_VALUE; 392 long end = Long.MIN_VALUE; 393 394 for (DocTree t : inheritedText.get(0)) { 395 start = Math.min(start, trees.getSourcePositions().getStartPosition(null, inheritedDocTree, t) - offset); 396 end = Math.max(end, trees.getSourcePositions().getEndPosition(null, inheritedDocTree, t) - offset); 397 } 398 String text = inherited.substring((int) start, (int) end); 399 400 if (syntheticTrees.containsKey(parent)) { 401 replace.put(new int[] {(int) lastPos + 1, (int) lastPos}, "\n" + syntheticTrees.get(parent) + text); 402 } else { 403 long inheritedStart = trees.getSourcePositions().getStartPosition(null, dcTree, node); 404 long inheritedEnd = trees.getSourcePositions().getEndPosition(null, dcTree, node); 405 406 replace.put(new int[] {(int) inheritedStart, (int) inheritedEnd}, text); 407 } 408 } 409 return super.visitInheritDoc(node, p); 410 } 411 private boolean inSynthetic; 412 @Override @DefinedBy(Api.COMPILER_TREE) 413 public Void scan(DocTree tree, Void p) { 414 if (exception[0] != null) { 415 return null; 416 } 417 boolean prevInSynthetic = inSynthetic; 418 try { 419 inSynthetic |= syntheticTrees.containsKey(tree); 420 return super.scan(tree, p); 421 } finally { 422 if (!inSynthetic) { 423 lastPos = trees.getSourcePositions().getEndPosition(null, dcTree, tree); 424 } 425 inSynthetic = prevInSynthetic; 426 } 427 } 428 429 private void insertTag(List<DocTree> tags, DocTree toInsert, List<String> parameters, List<String> throwsTypes) { 430 Comparator<DocTree> comp = (tag1, tag2) -> { 431 if (tag1.getKind() == tag2.getKind()) { 432 switch (toInsert.getKind()) { 433 case PARAM: { 434 ParamTree p1 = (ParamTree) tag1; 435 ParamTree p2 = (ParamTree) tag2; 436 int i1 = parameters.indexOf(p1.getName().getName().toString()); 437 int i2 = parameters.indexOf(p2.getName().getName().toString()); 438 439 return i1 - i2; 440 } 441 case THROWS: { 442 ThrowsTree t1 = (ThrowsTree) tag1; 443 ThrowsTree t2 = (ThrowsTree) tag2; 444 int i1 = throwsTypes.indexOf(getThrownException(task, el, docCommentTree, t1)); 445 int i2 = throwsTypes.indexOf(getThrownException(task, el, docCommentTree, t2)); 446 447 return i1 - i2; 448 } 449 } 450 } 451 452 int i1 = tagOrder.indexOf(tag1.getKind()); 453 int i2 = tagOrder.indexOf(tag2.getKind()); 454 455 return i1 - i2; 456 }; 457 458 for (int i = 0; i < tags.size(); i++) { 459 if (comp.compare(tags.get(i), toInsert) >= 0) { 460 tags.add(i, toInsert); 461 return ; 462 } 463 } 464 tags.add(toInsert); 465 } 466 467 private final List<DocTree.Kind> tagOrder = Arrays.asList(DocTree.Kind.PARAM, DocTree.Kind.THROWS, DocTree.Kind.RETURN); 468 }.scan(docCommentTree, null); 469 470 if (replace.isEmpty()) 471 return docComment; 472 473 StringBuilder replacedInheritDoc = new StringBuilder(docComment); 474 int offset = (int) trees.getSourcePositions().getStartPosition(null, docCommentTree, docCommentTree); 475 476 for (Entry<int[], String> e : replace.entrySet()) { 477 replacedInheritDoc.delete(e.getKey()[0] - offset, e.getKey()[1] - offset + 1); 478 replacedInheritDoc.insert(e.getKey()[0] - offset, e.getValue()); 479 } 480 481 return replacedInheritDoc.toString(); 482 } 483 484 private Stream<Element> superTypeForInheritDoc(JavacTask task, Element type) { 485 TypeElement clazz = (TypeElement) type; 486 Stream<Element> result = interfaces(clazz); 487 result = Stream.concat(result, interfaces(clazz).flatMap(el -> superTypeForInheritDoc(task, el))); 488 489 if (clazz.getSuperclass().getKind() == TypeKind.DECLARED) { 490 Element superClass = ((DeclaredType) clazz.getSuperclass()).asElement(); 491 result = Stream.concat(result, Stream.of(superClass)); 492 result = Stream.concat(result, superTypeForInheritDoc(task, superClass)); 493 } 494 495 return result; 496 } 497 //where: 498 private Stream<Element> interfaces(TypeElement clazz) { 499 return clazz.getInterfaces() 500 .stream() 501 .filter(tm -> tm.getKind() == TypeKind.DECLARED) 502 .map(tm -> ((DeclaredType) tm).asElement()); 503 } 504 505 private DocTree parseBlockTag(JavacTask task, String blockTag) { 506 DocCommentTree dc = parseDocComment(task, blockTag); 507 508 return dc.getBlockTags().get(0); 509 } 510 511 private DocCommentTree parseDocComment(JavacTask task, String javadoc) { 512 DocTrees trees = DocTrees.instance(task); 513 try { 514 return trees.getDocCommentTree(new SimpleJavaFileObject(new URI("mem://doc.html"), javax.tools.JavaFileObject.Kind.HTML) { 515 @Override @DefinedBy(Api.COMPILER) 516 public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { 517 return "<body>" + javadoc + "</body>"; 518 } 519 }); 520 } catch (URISyntaxException ex) { 521 return null; 522 } 523 } 524 525 private String getThrownException(JavacTask task, TreePath rootOn, DocCommentTree comment, ThrowsTree tt) { 526 DocTrees trees = DocTrees.instance(task); 527 Element exc = trees.getElement(new DocTreePath(new DocTreePath(rootOn, comment), tt.getExceptionName())); 528 return exc != null ? exc.toString() : null; 529 } 530 531 private Pair<JavacTask, TreePath> getSourceElement(JavacTask origin, Element el) throws IOException { 532 String handle = elementSignature(el); 533 Pair<JavacTask, TreePath> cached = signature2Source.get(handle); 534 535 if (cached != null) { 536 return cached.fst != null ? cached : null; 537 } 538 539 TypeElement type = topLevelType(el); 540 541 if (type == null) 542 return null; 543 544 String binaryName = origin.getElements().getBinaryName(type).toString(); 545 Pair<JavacTask, CompilationUnitTree> source = findSource(binaryName); 546 547 if (source == null) 548 return null; 549 550 fillElementCache(source.fst, source.snd); 551 552 cached = signature2Source.get(handle); 553 554 if (cached != null) { 555 return cached; 556 } else { 557 signature2Source.put(handle, Pair.of(null, null)); 558 return null; 559 } 560 } 561 //where: 562 private String elementSignature(Element el) { 563 switch (el.getKind()) { 564 case ANNOTATION_TYPE: case CLASS: case ENUM: case INTERFACE: 565 return ((TypeElement) el).getQualifiedName().toString(); 566 case FIELD: 567 return elementSignature(el.getEnclosingElement()) + "." + el.getSimpleName() + ":" + el.asType(); 568 case ENUM_CONSTANT: 569 return elementSignature(el.getEnclosingElement()) + "." + el.getSimpleName(); 570 case EXCEPTION_PARAMETER: case LOCAL_VARIABLE: case PARAMETER: case RESOURCE_VARIABLE: 571 return el.getSimpleName() + ":" + el.asType(); 572 case CONSTRUCTOR: case METHOD: 573 StringBuilder header = new StringBuilder(); 574 header.append(elementSignature(el.getEnclosingElement())); 575 if (el.getKind() == ElementKind.METHOD) { 576 header.append("."); 577 header.append(el.getSimpleName()); 578 } 579 header.append("("); 580 String sep = ""; 581 ExecutableElement method = (ExecutableElement) el; 582 for (Iterator<? extends VariableElement> i = method.getParameters().iterator(); i.hasNext();) { 583 VariableElement p = i.next(); 584 header.append(sep); 585 header.append(p.asType()); 586 sep = ", "; 587 } 588 header.append(")"); 589 return header.toString(); 590 default: 591 return el.toString(); 592 } 593 } 594 595 private TypeElement topLevelType(Element el) { 596 if (el.getKind() == ElementKind.PACKAGE) 597 return null; 598 599 while (el != null && el.getEnclosingElement().getKind() != ElementKind.PACKAGE) { 600 el = el.getEnclosingElement(); 601 } 602 603 return el != null && (el.getKind().isClass() || el.getKind().isInterface()) ? (TypeElement) el : null; 604 } 605 606 private void fillElementCache(JavacTask task, CompilationUnitTree cut) throws IOException { 607 Trees trees = Trees.instance(task); 608 609 new TreePathScanner<Void, Void>() { 610 @Override @DefinedBy(Api.COMPILER_TREE) 611 public Void visitMethod(MethodTree node, Void p) { 612 handleDeclaration(); 613 return null; 614 } 615 616 @Override @DefinedBy(Api.COMPILER_TREE) 617 public Void visitClass(ClassTree node, Void p) { 618 handleDeclaration(); 619 return super.visitClass(node, p); 620 } 621 622 @Override @DefinedBy(Api.COMPILER_TREE) 623 public Void visitVariable(VariableTree node, Void p) { 624 handleDeclaration(); 625 return super.visitVariable(node, p); 626 } 627 628 private void handleDeclaration() { 629 Element currentElement = trees.getElement(getCurrentPath()); 630 631 if (currentElement != null) { 632 signature2Source.put(elementSignature(currentElement), Pair.of(task, getCurrentPath())); 633 } 634 } 635 }.scan(cut, null); 636 } 637 638 private Pair<JavacTask, CompilationUnitTree> findSource(String binaryName) throws IOException { 639 JavaFileObject jfo = fm.getJavaFileForInput(StandardLocation.SOURCE_PATH, 640 binaryName, 641 JavaFileObject.Kind.SOURCE); 642 643 if (jfo == null) 644 return null; 645 646 List<JavaFileObject> jfos = Arrays.asList(jfo); 647 JavacTaskImpl task = (JavacTaskImpl) compiler.getTask(null, baseFileManager, d -> {}, null, null, jfos); 648 Iterable<? extends CompilationUnitTree> cuts = task.parse(); 649 650 task.enter(); 651 652 return Pair.of(task, cuts.iterator().next()); 653 } 654 655 @Override 656 public void close() throws IOException { 657 fm.close(); 658 } 659 } 660 661} 662