1 /* 2 * Copyright (c) 2015, 2017, 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 */ 25 26 package jdk.javadoc.internal.doclets.toolkit.util; 27 28 import java.util.ArrayList; 29 import java.util.Collections; 30 import java.util.List; 31 32 import javax.lang.model.element.Element; 33 import javax.lang.model.element.ExecutableElement; 34 import javax.lang.model.element.ModuleElement; 35 import javax.lang.model.element.Name; 36 import javax.lang.model.element.PackageElement; 37 import javax.lang.model.element.TypeElement; 38 import javax.lang.model.type.TypeMirror; 39 40 import com.sun.source.doctree.AttributeTree; 41 import com.sun.source.doctree.AttributeTree.ValueKind; 42 import com.sun.source.doctree.AuthorTree; 43 import com.sun.source.doctree.BlockTagTree; 44 import com.sun.source.doctree.CommentTree; 45 import com.sun.source.doctree.DeprecatedTree; 46 import com.sun.source.doctree.DocCommentTree; 47 import com.sun.source.doctree.DocTree; 48 import com.sun.source.doctree.EndElementTree; 49 import com.sun.source.doctree.EntityTree; 50 import com.sun.source.doctree.IdentifierTree; 51 import com.sun.source.doctree.InlineTagTree; 52 import com.sun.source.doctree.LinkTree; 53 import com.sun.source.doctree.LiteralTree; 54 import com.sun.source.doctree.ParamTree; 55 import com.sun.source.doctree.ProvidesTree; 56 import com.sun.source.doctree.ReferenceTree; 57 import com.sun.source.doctree.ReturnTree; 58 import com.sun.source.doctree.SeeTree; 59 import com.sun.source.doctree.SerialDataTree; 60 import com.sun.source.doctree.SerialFieldTree; 61 import com.sun.source.doctree.SerialTree; 62 import com.sun.source.doctree.SinceTree; 63 import com.sun.source.doctree.StartElementTree; 64 import com.sun.source.doctree.TextTree; 65 import com.sun.source.doctree.ThrowsTree; 66 import com.sun.source.doctree.UnknownBlockTagTree; 67 import com.sun.source.doctree.UsesTree; 68 import com.sun.source.doctree.ValueTree; 69 import com.sun.source.doctree.VersionTree; 70 import com.sun.source.util.DocTreePath; 71 import com.sun.source.util.DocTrees; 72 import com.sun.source.util.SimpleDocTreeVisitor; 73 import com.sun.source.util.TreePath; 74 import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration; 75 76 import static com.sun.source.doctree.DocTree.Kind.*; 77 78 /** 79 * A utility class. 80 * 81 * <p><b>This is NOT part of any supported API. 82 * If you write code that depends on this, you do so at your own risk. 83 * This code and its internal interfaces are subject to change or 84 * deletion without notice.</b> 85 */ 86 public class CommentHelper { 87 public final TreePath path; 88 public final DocCommentTree dctree; 89 public final Element element; 90 private Element overriddenElement; 91 92 public static final String SPACER = " "; 93 94 public CommentHelper(BaseConfiguration configuration, Element element, TreePath path, DocCommentTree dctree) { 95 //this.configuration = configuration; 96 this.element = element; 97 this.path = path; 98 this.dctree = dctree; 99 } 100 101 public void setOverrideElement(Element ove) { 102 if (this.element == ove) { 103 throw new AssertionError("cannot set given element as overridden element"); 104 } 105 overriddenElement = ove; 106 } 107 108 @SuppressWarnings("fallthrough") 109 public String getTagName(DocTree dtree) { 110 switch (dtree.getKind()) { 111 case AUTHOR: 112 case DEPRECATED: 113 case PARAM: 114 case PROVIDES: 115 case RETURN: 116 case SEE: 117 case SERIAL_DATA: 118 case SERIAL_FIELD: 119 case THROWS: 120 case UNKNOWN_BLOCK_TAG: 121 case USES: 122 case VERSION: 123 return ((BlockTagTree)dtree).getTagName(); 124 case UNKNOWN_INLINE_TAG: 125 return ((InlineTagTree)dtree).getTagName(); 126 case ERRONEOUS: 127 return "erroneous"; 128 default: 129 return dtree.getKind().tagName; 130 } 131 } 132 133 public boolean isTypeParameter(DocTree dtree) { 134 if (dtree.getKind() == PARAM) { 135 return ((ParamTree)dtree).isTypeParameter(); 136 } 137 return false; 138 } 139 140 public String getParameterName(DocTree dtree) { 141 if (dtree.getKind() == PARAM) { 142 return ((ParamTree) dtree).getName().toString(); 143 } else { 144 return null; 145 } 146 } 147 148 Element getElement(BaseConfiguration c, ReferenceTree rtree) { 149 // likely a synthesized tree 150 if (path == null) { 151 // NOTE: this code path only supports module/package/type signatures 152 // and not member signatures. For more complete support, 153 // set a suitable path and avoid this branch. 154 TypeMirror symbol = c.utils.getSymbol(rtree.getSignature()); 155 if (symbol == null) { 156 return null; 157 } 158 return c.docEnv.getTypeUtils().asElement(symbol); 159 } 160 // case A: the element contains no comments associated and 161 // the comments need to be copied from ancestor 162 // case B: the element has @inheritDoc, then the ancestral comment 163 // as appropriate has to be copied over. 164 165 // Case A. 166 if (dctree == null && overriddenElement != null) { 167 CommentHelper ovch = c.utils.getCommentHelper(overriddenElement); 168 return ovch.getElement(c, rtree); 169 } 170 if (dctree == null) { 171 return null; 172 } 173 DocTreePath docTreePath = DocTreePath.getPath(path, dctree, rtree); 174 if (docTreePath == null) { 175 // Case B. 176 if (overriddenElement != null) { 177 CommentHelper ovch = c.utils.getCommentHelper(overriddenElement); 178 return ovch.getElement(c, rtree); 179 } 180 return null; 181 } 182 DocTrees doctrees = c.docEnv.getDocTrees(); 183 return doctrees.getElement(docTreePath); 184 } 185 186 public Element getException(BaseConfiguration c, DocTree dtree) { 187 if (dtree.getKind() == THROWS || dtree.getKind() == EXCEPTION) { 188 ThrowsTree tt = (ThrowsTree)dtree; 189 ReferenceTree exceptionName = tt.getExceptionName(); 190 return getElement(c, exceptionName); 191 } 192 return null; 193 } 194 195 public List<? extends DocTree> getDescription(BaseConfiguration c, DocTree dtree) { 196 return getTags(c, dtree); 197 } 198 199 public String getText(List<? extends DocTree> list) { 200 StringBuilder sb = new StringBuilder(); 201 for (DocTree dt : list) { 202 sb.append(getText0(dt)); 203 } 204 return sb.toString(); 205 } 206 207 public String getText(DocTree dt) { 208 return getText0(dt).toString(); 209 } 210 211 private StringBuilder getText0(DocTree dt) { 212 final StringBuilder sb = new StringBuilder(); 213 new SimpleDocTreeVisitor<Void, Void>() { 214 @Override 215 public Void visitAttribute(AttributeTree node, Void p) { 216 sb.append(SPACER).append(node.getName()); 217 if (node.getValueKind() == ValueKind.EMPTY) { 218 return null; 219 } 220 221 sb.append("="); 222 String quote; 223 switch (node.getValueKind()) { 224 case DOUBLE: 225 quote = "\""; 226 break; 227 case SINGLE: 228 quote = "\'"; 229 break; 230 default: 231 quote = ""; 232 break; 233 } 234 sb.append(quote); 235 node.getValue().stream().forEach((dt) -> { 236 dt.accept(this, null); 237 }); 238 sb.append(quote); 239 return null; 240 } 241 242 @Override 243 public Void visitEndElement(EndElementTree node, Void p) { 244 sb.append("</") 245 .append(node.getName()) 246 .append(">"); 247 return null; 248 } 249 250 @Override 251 public Void visitEntity(EntityTree node, Void p) { 252 sb.append(node.toString()); 253 return null; 254 } 255 256 @Override 257 public Void visitLink(LinkTree node, Void p) { 258 if (node.getReference() == null) { 259 return null; 260 } 261 262 node.getReference().accept(this, null); 263 node.getLabel().stream().forEach((dt) -> { 264 dt.accept(this, null); 265 }); 266 return null; 267 } 268 269 @Override 270 public Void visitLiteral(LiteralTree node, Void p) { 271 if (node.getKind() == CODE) { 272 sb.append("<").append(node.getKind().tagName).append(">"); 273 } 274 sb.append(node.getBody().toString()); 275 if (node.getKind() == CODE) { 276 sb.append("</").append(node.getKind().tagName).append(">"); 277 } 278 return null; 279 } 280 281 @Override 282 public Void visitReference(ReferenceTree node, Void p) { 283 sb.append(node.getSignature()); 284 return null; 285 } 286 287 @Override 288 public Void visitSee(SeeTree node, Void p) { 289 node.getReference().stream().forEach((dt) -> { 290 dt.accept(this, null); 291 }); 292 return null; 293 } 294 295 @Override 296 public Void visitSerial(SerialTree node, Void p) { 297 node.getDescription().stream().forEach((dt) -> { 298 dt.accept(this, null); 299 }); 300 return null; 301 } 302 303 @Override 304 public Void visitStartElement(StartElementTree node, Void p) { 305 sb.append("<"); 306 sb.append(node.getName()); 307 node.getAttributes().stream().forEach((dt) -> { 308 dt.accept(this, null); 309 }); 310 sb.append((node.isSelfClosing() ? "/>" : ">")); 311 return null; 312 } 313 314 @Override 315 public Void visitText(TextTree node, Void p) { 316 sb.append(node.getBody()); 317 return null; 318 } 319 320 @Override 321 public Void visitUnknownBlockTag(UnknownBlockTagTree node, Void p) { 322 node.getContent().stream().forEach((dt) -> { 323 dt.accept(this, null); 324 }); 325 return null; 326 } 327 328 @Override 329 public Void visitValue(ValueTree node, Void p) { 330 return node.getReference().accept(this, null); 331 } 332 333 @Override 334 protected Void defaultAction(DocTree node, Void p) { 335 sb.append(node.toString()); 336 return null; 337 } 338 }.visit(dt, null); 339 return sb; 340 } 341 342 public String getLabel(BaseConfiguration c, DocTree dtree) { 343 return new SimpleDocTreeVisitor<String, Void>() { 344 @Override 345 public String visitLink(LinkTree node, Void p) { 346 StringBuilder sb = new StringBuilder(); 347 node.getLabel().stream().forEach((dt) -> { 348 sb.append(getText(dt)); 349 }); 350 return sb.toString(); 351 } 352 353 @Override 354 public String visitSee(SeeTree node, Void p) { 355 StringBuilder sb = new StringBuilder(); 356 node.getReference().stream().filter((dt) -> (c.utils.isText(dt))).forEach((dt) -> { 357 sb.append(((TextTree)dt).getBody()); 358 }); 359 return sb.toString(); 360 } 361 362 @Override 363 protected String defaultAction(DocTree node, Void p) { 364 return ""; 365 } 366 }.visit(dtree, null); 367 } 368 369 public TypeElement getReferencedClass(BaseConfiguration c, DocTree dtree) { 370 Element e = getReferencedElement(c, dtree); 371 if (e == null) { 372 return null; 373 } else if (c.utils.isTypeElement(e)) { 374 return (TypeElement) e; 375 } else if (!c.utils.isPackage(e)) { 376 return c.utils.getEnclosingTypeElement(e); 377 } 378 return null; 379 } 380 381 public String getReferencedClassName(BaseConfiguration c, DocTree dtree) { 382 Element e = getReferencedClass(c, dtree); 383 if (e != null) { 384 return c.utils.isTypeElement(e) ? c.utils.getSimpleName(e) : null; 385 } 386 String s = getReferencedSignature(dtree); 387 if (s == null) { 388 return null; 389 } 390 int n = s.indexOf("#"); 391 return (n == -1) ? s : s.substring(0, n); 392 } 393 394 public Element getReferencedMember(BaseConfiguration c, DocTree dtree) { 395 Element e = getReferencedElement(c, dtree); 396 if (e == null) { 397 return null; 398 } 399 return (c.utils.isExecutableElement(e) || c.utils.isVariableElement(e)) ? e : null; 400 } 401 402 public String getReferencedMemberName(DocTree dtree) { 403 String s = getReferencedSignature(dtree); 404 if (s == null) { 405 return null; 406 } 407 int n = s.indexOf("#"); 408 return (n == -1) ? null : s.substring(n + 1); 409 } 410 411 public String getReferencedMemberName(BaseConfiguration c, Element e) { 412 if (e == null) { 413 return null; 414 } 415 return c.utils.isExecutableElement(e) 416 ? c.utils.getSimpleName(e) + c.utils.makeSignature((ExecutableElement) e, true, true) 417 : c.utils.getSimpleName(e); 418 } 419 420 public PackageElement getReferencedPackage(BaseConfiguration c, DocTree dtree) { 421 Element e = getReferencedElement(c, dtree); 422 if (e != null) { 423 return c.utils.containingPackage(e); 424 } 425 return null; 426 } 427 428 public List<? extends DocTree> getFirstSentenceTrees(BaseConfiguration c, List<? extends DocTree> body) { 429 List<DocTree> firstSentence = c.docEnv.getDocTrees().getFirstSentence(body); 430 return firstSentence; 431 } 432 433 public List<? extends DocTree> getFirstSentenceTrees(BaseConfiguration c, DocTree dtree) { 434 return getFirstSentenceTrees(c, getBody(c, dtree)); 435 } 436 437 private Element getReferencedElement(BaseConfiguration c, DocTree dtree) { 438 return new SimpleDocTreeVisitor<Element, Void>() { 439 @Override 440 public Element visitSee(SeeTree node, Void p) { 441 for (DocTree dt : node.getReference()) { 442 return visit(dt, null); 443 } 444 return null; 445 } 446 447 @Override 448 public Element visitLink(LinkTree node, Void p) { 449 return visit(node.getReference(), null); 450 } 451 452 @Override 453 public Element visitProvides(ProvidesTree node, Void p) { 454 return visit(node.getServiceType(), null); 455 } 456 457 @Override 458 public Element visitValue(ValueTree node, Void p) { 459 return visit(node.getReference(), null); 460 } 461 462 @Override 463 public Element visitReference(ReferenceTree node, Void p) { 464 return getElement(c, node); 465 } 466 467 @Override 468 public Element visitSerialField(SerialFieldTree node, Void p) { 469 return visit(node.getType(), null); 470 } 471 472 @Override 473 public Element visitUses(UsesTree node, Void p) { 474 return visit(node.getServiceType(), null); 475 } 476 477 @Override 478 protected Element defaultAction(DocTree node, Void p) { 479 return null; 480 } 481 }.visit(dtree, null); 482 } 483 484 public TypeElement getServiceType(BaseConfiguration c, DocTree dtree) { 485 Element e = getReferencedElement(c, dtree); 486 if (e != null) { 487 return c.utils.isTypeElement(e) ? (TypeElement) e : null; 488 } 489 return null; 490 } 491 492 public String getReferencedSignature(DocTree dtree) { 493 return new SimpleDocTreeVisitor<String, Void>() { 494 @Override 495 public String visitSee(SeeTree node, Void p) { 496 for (DocTree dt : node.getReference()) { 497 return visit(dt, null); 498 } 499 return null; 500 } 501 502 @Override 503 public String visitLink(LinkTree node, Void p) { 504 return visit(node.getReference(), null); 505 } 506 507 @Override 508 public String visitValue(ValueTree node, Void p) { 509 return visit(node.getReference(), null); 510 } 511 512 @Override 513 public String visitReference(ReferenceTree node, Void p) { 514 return node.getSignature(); 515 } 516 517 @Override 518 public String visitSerialField(SerialFieldTree node, Void p) { 519 return visit(node.getType(), null); 520 } 521 522 @Override 523 protected String defaultAction(DocTree node, Void p) { 524 return null; 525 } 526 }.visit(dtree, null); 527 } 528 529 public List<? extends DocTree> getReference(DocTree dtree) { 530 return dtree.getKind() == SEE ? ((SeeTree)dtree).getReference() : null; 531 } 532 533 public ReferenceTree getExceptionName(DocTree dtree) { 534 return (dtree.getKind() == THROWS || dtree.getKind() == EXCEPTION) 535 ? ((ThrowsTree)dtree).getExceptionName() 536 : null; 537 } 538 539 public IdentifierTree getName(DocTree dtree) { 540 switch (dtree.getKind()) { 541 case PARAM: 542 return ((ParamTree)dtree).getName(); 543 case SERIAL_FIELD: 544 return ((SerialFieldTree)dtree).getName(); 545 default: 546 return null; 547 } 548 } 549 550 public List<? extends DocTree> getTags(BaseConfiguration c, DocTree dtree) { 551 return new SimpleDocTreeVisitor<List<? extends DocTree>, Void>() { 552 List<? extends DocTree> asList(String content) { 553 List<DocTree> out = new ArrayList<>(); 554 out.add(c.cmtUtils.makeTextTree(content)); 555 return out; 556 } 557 558 @Override 559 public List<? extends DocTree> visitAuthor(AuthorTree node, Void p) { 560 return node.getName(); 561 } 562 563 @Override 564 public List<? extends DocTree> visitComment(CommentTree node, Void p) { 565 return asList(node.getBody()); 566 } 567 568 @Override 569 public List<? extends DocTree> visitDeprecated(DeprecatedTree node, Void p) { 570 return node.getBody(); 571 } 572 573 @Override 574 public List<? extends DocTree> visitDocComment(DocCommentTree node, Void p) { 575 return node.getBody(); 576 } 577 578 @Override 579 public List<? extends DocTree> visitLiteral(LiteralTree node, Void p) { 580 return asList(node.getBody().getBody()); 581 } 582 583 @Override 584 public List<? extends DocTree> visitProvides(ProvidesTree node, Void p) { 585 return node.getDescription(); 586 } 587 588 @Override 589 public List<? extends DocTree> visitSince(SinceTree node, Void p) { 590 return node.getBody(); 591 } 592 593 @Override 594 public List<? extends DocTree> visitText(TextTree node, Void p) { 595 return asList(node.getBody()); 596 } 597 598 @Override 599 public List<? extends DocTree> visitVersion(VersionTree node, Void p) { 600 return node.getBody(); 601 } 602 603 @Override 604 public List<? extends DocTree> visitParam(ParamTree node, Void p) { 605 return node.getDescription(); 606 } 607 608 @Override 609 public List<? extends DocTree> visitReturn(ReturnTree node, Void p) { 610 return node.getDescription(); 611 } 612 613 @Override 614 public List<? extends DocTree> visitSee(SeeTree node, Void p) { 615 return node.getReference(); 616 } 617 618 @Override 619 public List<? extends DocTree> visitSerial(SerialTree node, Void p) { 620 return node.getDescription(); 621 } 622 623 @Override 624 public List<? extends DocTree> visitSerialData(SerialDataTree node, Void p) { 625 return node.getDescription(); 626 } 627 628 @Override 629 public List<? extends DocTree> visitSerialField(SerialFieldTree node, Void p) { 630 return node.getDescription(); 631 } 632 633 @Override 634 public List<? extends DocTree> visitThrows(ThrowsTree node, Void p) { 635 return node.getDescription(); 636 } 637 638 @Override 639 public List<? extends DocTree> visitUnknownBlockTag(UnknownBlockTagTree node, Void p) { 640 return node.getContent(); 641 } 642 643 @Override 644 public List<? extends DocTree> visitUses(UsesTree node, Void p) { 645 return node.getDescription(); 646 } 647 648 @Override 649 protected List<? extends DocTree> defaultAction(DocTree node, Void p) { 650 return Collections.emptyList(); 651 } 652 }.visit(dtree, null); 653 } 654 655 public List<? extends DocTree> getBody(BaseConfiguration c, DocTree dtree) { 656 return getTags(c, dtree); 657 } 658 659 public ReferenceTree getType(DocTree dtree) { 660 if (dtree.getKind() == SERIAL_FIELD) { 661 return ((SerialFieldTree)dtree).getType(); 662 } else { 663 return null; 664 } 665 } 666 667 public DocTreePath getDocTreePath(DocTree dtree) { 668 if (path == null || dctree == null || dtree == null) 669 return null; 670 return DocTreePath.getPath(path, dctree, dtree); 671 } 672 673 public Element getOverriddenElement() { 674 return overriddenElement; 675 } 676 677 678 /** 679 * For debugging purposes only. Do not rely on this for other things. 680 * @return a string representation. 681 */ 682 @Override 683 public String toString() { 684 StringBuilder sb = new StringBuilder("CommentHelper{" + "path=" + path + ", dctree=" + dctree); 685 sb.append(", element="); 686 sb.append(element.getEnclosingElement()); 687 sb.append("::"); 688 sb.append(element); 689 sb.append(", overriddenElement="); 690 if (overriddenElement != null) { 691 sb.append(overriddenElement.getEnclosingElement()); 692 sb.append("::"); 693 sb.append(overriddenElement); 694 } else { 695 sb.append("<none>"); 696 } 697 sb.append('}'); 698 return sb.toString(); 699 } 700 }