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