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