001/* 002 * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. Oracle designates this 008 * particular file as subject to the "Classpath" exception as provided 009 * by Oracle in the LICENSE file that accompanied this code. 010 * 011 * This code is distributed in the hope that it will be useful, but WITHOUT 012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 014 * version 2 for more details (a copy is included in the LICENSE file that 015 * accompanied this code). 016 * 017 * You should have received a copy of the GNU General Public License version 018 * 2 along with this work; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 020 * 021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 022 * or visit www.oracle.com if you need additional information or have any 023 * questions. 024 */ 025 026package org.jdrupes.mdoclet.internal.doclets.toolkit; 027 028import java.net.URI; 029import java.util.ArrayList; 030import java.util.HashMap; 031import java.util.List; 032import java.util.regex.Matcher; 033import java.util.regex.Pattern; 034 035import javax.lang.model.element.Element; 036import javax.lang.model.element.ExecutableElement; 037import javax.lang.model.element.Name; 038import javax.lang.model.element.PackageElement; 039import javax.lang.model.element.RecordComponentElement; 040import javax.lang.model.element.TypeElement; 041import javax.lang.model.element.VariableElement; 042import javax.lang.model.util.Elements; 043import javax.tools.FileObject; 044import javax.tools.JavaFileObject; 045import javax.tools.SimpleJavaFileObject; 046 047import org.jdrupes.mdoclet.internal.doclets.toolkit.util.Utils; 048import org.jdrupes.mdoclet.internal.doclets.toolkit.util.VisibleMemberTable; 049 050import com.sun.source.doctree.AttributeTree; 051import com.sun.source.doctree.DocCommentTree; 052import com.sun.source.doctree.DocTree; 053import com.sun.source.doctree.IdentifierTree; 054import com.sun.source.doctree.LiteralTree; 055import com.sun.source.doctree.ParamTree; 056import com.sun.source.doctree.ReferenceTree; 057import com.sun.source.doctree.ReturnTree; 058import com.sun.source.doctree.SinceTree; 059import com.sun.source.doctree.TextTree; 060import com.sun.source.doctree.UnknownBlockTagTree; 061import com.sun.source.util.DocTreeFactory; 062import com.sun.source.util.DocTreePath; 063import com.sun.source.util.DocTrees; 064import com.sun.source.util.TreePath; 065import com.sun.tools.javac.util.DefinedBy; 066import com.sun.tools.javac.util.DefinedBy.Api; 067 068/** 069 * A utility class for handling documentation comments. 070 */ 071public class CommentUtils { 072 073 final BaseConfiguration configuration; 074 final Utils utils; 075 final Resources resources; 076 final DocTreeFactory treeFactory; 077 final DocTrees trees; 078 final Elements elementUtils; 079 080 /** 081 * A map for storing automatically generated comments for various 082 * elements, such as mandated elements (Enum.values, Enum.valueOf, etc) 083 * and JavaFX properties. 084 * 085 * @see Utils#dcTreeCache 086 */ 087 final HashMap<Element, DocCommentInfo> dcInfoMap = new HashMap<>(); 088 089 protected CommentUtils(BaseConfiguration configuration) { 090 this.configuration = configuration; 091 utils = configuration.utils; 092 resources = configuration.getDocResources(); 093 trees = configuration.docEnv.getDocTrees(); 094 treeFactory = trees.getDocTreeFactory(); 095 elementUtils = configuration.docEnv.getElementUtils(); 096 } 097 098 public List<? extends DocTree> 099 makePropertyDescriptionTree(List<? extends DocTree> content) { 100 Name name = elementUtils.getName("propertyDescription"); 101 return List.of(treeFactory.newUnknownBlockTagTree(name, content)); 102 } 103 104 public LiteralTree makeCodeTree(String text) { 105 return treeFactory.newCodeTree(makeTextTree(text)); 106 } 107 108 public List<? extends DocTree> makeFirstSentenceTree(String content) { 109 return List.of(treeFactory.newTextTree(content)); 110 } 111 112 public ParamTree makeParamTree(Name name, 113 List<? extends DocTree> description) { 114 return treeFactory.newParamTree(false, 115 treeFactory.newIdentifierTree(name), description); 116 } 117 118 public ReturnTree makeReturnTree(List<? extends DocTree> description) { 119 return treeFactory.newReturnTree(false, description); 120 } 121 122 public DocTree makeSeeTree(String sig, Element e) { 123 return treeFactory 124 .newSeeTree(List.of(treeFactory.newReferenceTree(sig))); 125 } 126 127 public TextTree makeTextTree(String content) { 128 return treeFactory.newTextTree(content); 129 } 130 131 public TextTree makeTextTreeForResource(String key) { 132 return treeFactory.newTextTree(resources.getText(key)); 133 } 134 135 /** 136 * Parses a string, looking for simple embedded HTML. 137 * @param s the string 138 * @return the list of parsed {@code DocTree} nodes 139 */ 140 private List<DocTree> parse(String s) { 141 List<DocTree> list = null; 142 Pattern p = Pattern.compile("(?i)<(/)?([a-z0-9]+)(/)?>"); 143 Matcher m = p.matcher(s); 144 int start = 0; 145 while (m.find()) { 146 if (list == null) { 147 list = new ArrayList<>(); 148 } 149 if (m.start() > 0) { 150 list.add( 151 treeFactory.newTextTree(s.substring(start, m.start()))); 152 } 153 Name name = elementUtils.getName(m.group(2)); 154 list.add(m.group(1) == null 155 ? treeFactory.newStartElementTree(name, List.of(), 156 m.group(3) != null) 157 : treeFactory.newEndElementTree(name)); 158 start = m.end(); 159 } 160 if (list == null) { 161 return List.of(treeFactory.newTextTree(s)); 162 } else { 163 if (start < s.length()) { 164 list.add( 165 treeFactory.newTextTree(s.substring(start, s.length()))); 166 } 167 return list; 168 } 169 } 170 171 public void setEnumValuesTree(ExecutableElement ee) { 172 List<DocTree> fullBody = new ArrayList<>(); 173 fullBody.add(treeFactory 174 .newTextTree(resources.getText("doclet.enum_values_doc.fullbody"))); 175 176 List<DocTree> descriptions = new ArrayList<>(); 177 descriptions.add(treeFactory 178 .newTextTree(resources.getText("doclet.enum_values_doc.return"))); 179 180 List<DocTree> tags = new ArrayList<>(); 181 tags.add(treeFactory.newReturnTree(descriptions)); 182 DocCommentTree docTree = treeFactory.newDocCommentTree(fullBody, tags); 183 dcInfoMap.put(ee, new DocCommentInfo(null, docTree)); 184 } 185 186 public void setEnumValueOfTree(ExecutableElement ee) { 187 List<DocTree> fullBody 188 = parse(resources.getText("doclet.enum_valueof_doc.fullbody")); 189 190 List<DocTree> tags = new ArrayList<>(); 191 192 List<DocTree> paramDescs = new ArrayList<>(); 193 paramDescs.add(treeFactory.newTextTree( 194 resources.getText("doclet.enum_valueof_doc.param_name"))); 195 java.util.List<? extends VariableElement> parameters 196 = ee.getParameters(); 197 VariableElement param = parameters.get(0); 198 IdentifierTree id = treeFactory.newIdentifierTree( 199 elementUtils.getName(param.getSimpleName().toString())); 200 tags.add(treeFactory.newParamTree(false, id, paramDescs)); 201 202 List<DocTree> returnDescs = new ArrayList<>(); 203 returnDescs.add(treeFactory 204 .newTextTree(resources.getText("doclet.enum_valueof_doc.return"))); 205 tags.add(treeFactory.newReturnTree(returnDescs)); 206 207 List<DocTree> throwsDescs = new ArrayList<>(); 208 throwsDescs.add(treeFactory.newTextTree( 209 resources.getText("doclet.enum_valueof_doc.throws_ila"))); 210 211 ReferenceTree ref = treeFactory 212 .newReferenceTree("java.lang.IllegalArgumentException"); 213 tags.add(treeFactory.newThrowsTree(ref, throwsDescs)); 214 215 throwsDescs = new ArrayList<>(); 216 throwsDescs.add(treeFactory.newTextTree( 217 resources.getText("doclet.enum_valueof_doc.throws_npe"))); 218 219 ref = treeFactory.newReferenceTree("java.lang.NullPointerException"); 220 tags.add(treeFactory.newThrowsTree(ref, throwsDescs)); 221 222 DocCommentTree docTree = treeFactory.newDocCommentTree(fullBody, tags); 223 224 dcInfoMap.put(ee, new DocCommentInfo(null, docTree)); 225 } 226 227 /** 228 * Generates the description for the canonical constructor for a record. 229 * @param ee the constructor 230 */ 231 public void setRecordConstructorTree(ExecutableElement ee) { 232 TypeElement te = utils.getEnclosingTypeElement(ee); 233 234 List<DocTree> fullBody = makeDescriptionWithName( 235 "doclet.record_constructor_doc.fullbody", te.getSimpleName()); 236 237 List<DocTree> tags = new ArrayList<>(); 238 for (VariableElement param : ee.getParameters()) { 239 Name name = param.getSimpleName(); 240 IdentifierTree id = treeFactory.newIdentifierTree(name); 241 tags.add(treeFactory.newParamTree(false, id, 242 makeDescriptionWithComponent( 243 "doclet.record_constructor_doc.param_name", te, name))); 244 } 245 246 DocCommentTree docTree = treeFactory.newDocCommentTree(fullBody, tags); 247 dcInfoMap.put(ee, new DocCommentInfo(null, docTree)); 248 } 249 250 /** 251 * Generates the description for the standard {@code equals} method for a record. 252 * @param ee the {@code equals} method 253 */ 254 public void setRecordEqualsTree(ExecutableElement ee) { 255 List<DocTree> fullBody = new ArrayList<>(); 256 add(fullBody, "doclet.record_equals_doc.fullbody.head"); 257 fullBody.add(treeFactory.newTextTree(" ")); 258 259 List<? extends RecordComponentElement> comps 260 = ((TypeElement) ee.getEnclosingElement()).getRecordComponents(); 261 boolean hasPrimitiveComponents 262 = comps.stream().anyMatch(e -> e.asType().getKind().isPrimitive()); 263 boolean hasReferenceComponents 264 = comps.stream().anyMatch(e -> !e.asType().getKind().isPrimitive()); 265 if (hasPrimitiveComponents && hasReferenceComponents) { 266 add(fullBody, "doclet.record_equals_doc.fullbody.tail.both"); 267 } else if (hasPrimitiveComponents) { 268 add(fullBody, "doclet.record_equals_doc.fullbody.tail.primitive"); 269 } else if (hasReferenceComponents) { 270 add(fullBody, "doclet.record_equals_doc.fullbody.tail.reference"); 271 } 272 Name paramName = ee.getParameters().get(0).getSimpleName(); 273 IdentifierTree id = treeFactory.newIdentifierTree(paramName); 274 List<DocTree> paramDesc = makeDescriptionWithName( 275 "doclet.record_equals_doc.param_name", paramName); 276 DocTree paramTree = treeFactory.newParamTree(false, id, paramDesc); 277 278 DocTree returnTree = treeFactory.newReturnTree( 279 makeDescriptionWithName("doclet.record_equals_doc.return", 280 paramName)); 281 282 TreePath treePath = utils.getTreePath(ee.getEnclosingElement()); 283 DocCommentTree docTree = treeFactory.newDocCommentTree(fullBody, 284 List.of(paramTree, returnTree)); 285 dcInfoMap.put(ee, new DocCommentInfo(treePath, docTree)); 286 } 287 288 private void add(List<DocTree> contents, String resourceKey) { 289 // Special case to allow '{@link ...}' to appear in the string. 290 // A less general case would be to detect literal use of Object.equals 291 // A more general case would be to allow access to DocCommentParser 292 // somehow 293 String body = resources.getText(resourceKey); 294 Pattern p = Pattern.compile("\\{@link (\\S*)(.*)}"); 295 Matcher m = p.matcher(body); 296 int start = 0; 297 while (m.find(start)) { 298 if (m.start() > start) { 299 contents.addAll(parse(body.substring(start, m.start()))); 300 } 301 ReferenceTree refTree = treeFactory.newReferenceTree(m.group(1)); 302 List<DocTree> descr = parse(m.group(2).trim()); 303 contents.add(treeFactory.newLinkTree(refTree, descr)); 304 start = m.end(); 305 } 306 if (start < body.length()) { 307 contents.addAll(parse(body.substring(start))); 308 } 309 } 310 311 /** 312 * Generates the description for the standard {@code hashCode} method for a record. 313 * @param ee the {@code hashCode} method 314 */ 315 public void setRecordHashCodeTree(ExecutableElement ee) { 316 List<DocTree> fullBody = List 317 .of(makeTextTreeForResource("doclet.record_hashCode_doc.fullbody")); 318 319 DocTree returnTree = treeFactory.newReturnTree( 320 List.of( 321 makeTextTreeForResource("doclet.record_hashCode_doc.return"))); 322 323 DocCommentTree docTree 324 = treeFactory.newDocCommentTree(fullBody, List.of(returnTree)); 325 dcInfoMap.put(ee, new DocCommentInfo(null, docTree)); 326 } 327 328 /** 329 * Generates the description for the standard {@code toString} method for a record. 330 * @param ee the {@code toString} method 331 */ 332 public void setRecordToStringTree(ExecutableElement ee) { 333 List<DocTree> fullBody = List.of( 334 treeFactory.newTextTree( 335 resources.getText("doclet.record_toString_doc.fullbody"))); 336 337 DocTree returnTree = treeFactory.newReturnTree(List.of( 338 treeFactory.newTextTree( 339 resources.getText("doclet.record_toString_doc.return")))); 340 341 DocCommentTree docTree 342 = treeFactory.newDocCommentTree(fullBody, List.of(returnTree)); 343 dcInfoMap.put(ee, new DocCommentInfo(null, docTree)); 344 } 345 346 /** 347 * Generates the description for the accessor method for a state component of a record. 348 * @param ee the accessor method 349 */ 350 public void setRecordAccessorTree(ExecutableElement ee) { 351 TypeElement te = utils.getEnclosingTypeElement(ee); 352 353 List<DocTree> fullBody = makeDescriptionWithComponent( 354 "doclet.record_accessor_doc.fullbody", te, ee.getSimpleName()); 355 356 DocTree returnTree = treeFactory.newReturnTree( 357 makeDescriptionWithComponent("doclet.record_accessor_doc.return", 358 te, ee.getSimpleName())); 359 360 DocCommentTree docTree 361 = treeFactory.newDocCommentTree(fullBody, List.of(returnTree)); 362 dcInfoMap.put(ee, new DocCommentInfo(null, docTree)); 363 } 364 365 /** 366 * Generates the description for the field for a state component of a record. 367 * @param ve the field 368 */ 369 public void setRecordFieldTree(VariableElement ve) { 370 TypeElement te = utils.getEnclosingTypeElement(ve); 371 372 List<DocTree> fullBody = makeDescriptionWithComponent( 373 "doclet.record_field_doc.fullbody", te, ve.getSimpleName()); 374 375 DocCommentTree docTree 376 = treeFactory.newDocCommentTree(fullBody, List.of()); 377 dcInfoMap.put(ve, new DocCommentInfo(null, docTree)); 378 } 379 380 /** 381 * Update the property method, property setter and/or property getter 382 * comment text so that it contains the documentation from 383 * the preferred property description (field or property method). 384 * The method adds the leading sentence, copied documentation including 385 * the defaultValue tag and the {@code @see} tags if the appropriate methods are 386 * available. 387 * 388 * @param member the member which is to be augmented 389 * @param property the element containing the preferred property description 390 */ 391 public void updatePropertyMethodComment(ExecutableElement member, 392 Element property) { 393 final String memberName = member.getSimpleName().toString(); 394 final boolean isSetter = memberName.startsWith("set"); 395 final boolean isGetter 396 = memberName.startsWith("get") || memberName.startsWith("is"); 397 398 List<DocTree> fullBody = new ArrayList<>(); 399 List<DocTree> blockTags = new ArrayList<>(); 400 401 if (isGetter || isSetter) { 402 DocTree propName = makeCodeTree(utils.propertyName(member)); 403 404 if (isGetter) { 405 // Set the body and @return 406 fullBody.addAll( 407 getComment("doclet.PropertyGetterWithName", propName)); 408 blockTags.add(makeReturnTree( 409 getComment("doclet.PropertyGetterReturn", propName))); 410 } 411 412 if (isSetter) { 413 // Set the body and @param 414 fullBody.addAll( 415 getComment("doclet.PropertySetterWithName", propName)); 416 VariableElement arg0 = member.getParameters().get(0); 417 blockTags.add(makeParamTree(arg0.getSimpleName(), 418 getComment("doclet.PropertySetterParam", propName))); 419 } 420 421 // Set the @propertyDescription 422 List<? extends DocTree> propertyTags = utils.getBlockTags(property, 423 t -> (t instanceof UnknownBlockTagTree tree) 424 && (tree.getTagName().equals("propertyDescription"))); 425 if (propertyTags.isEmpty()) { 426 List<? extends DocTree> comment = utils.getFullBody(property); 427 blockTags.addAll(makePropertyDescriptionTree(comment)); 428 } 429 } else { 430 // property method 431 fullBody.addAll(utils.getFullBody(property)); 432 433 // Set the @return 434 DocTree propName 435 = makeCodeTree(configuration.propertyUtils.getBaseName(member)); 436 List<? extends DocTree> returnTags 437 = utils.getBlockTags(property, DocTree.Kind.RETURN); 438 if (returnTags.isEmpty()) { 439 blockTags.add(makeReturnTree( 440 getComment("doclet.PropertyMethodReturn", propName))); 441 } else { 442 blockTags.addAll(returnTags); 443 } 444 } 445 446 // copy certain tags 447 List<? extends SinceTree> sinceTags 448 = utils.getBlockTags(property, DocTree.Kind.SINCE, SinceTree.class); 449 blockTags.addAll(sinceTags); 450 451 List<? extends DocTree> bTags = utils.getBlockTags(property, 452 t -> (t instanceof UnknownBlockTagTree tree) 453 && (tree.getTagName().equals("defaultValue"))); 454 blockTags.addAll(bTags); 455 456 // add @see tags 457 TypeElement te = (TypeElement) member.getEnclosingElement(); 458 VisibleMemberTable vmt = configuration.getVisibleMemberTable(te); 459 ExecutableElement getter = vmt.getPropertyGetter(member); 460 ExecutableElement setter = vmt.getPropertySetter(member); 461 ExecutableElement propMethod = vmt.getPropertyMethod(member); 462 463 if (getter != null && getter != member) { 464 String sig = "#" + getter.getSimpleName() + "()"; 465 blockTags.add(makeSeeTree(sig, getter)); 466 } 467 468 if (setter != null && setter != member) { 469 VariableElement param = setter.getParameters().get(0); 470 StringBuilder sb = new StringBuilder("#"); 471 sb.append(setter.getSimpleName()); 472 if (!utils.isTypeVariable(param.asType())) { 473 sb.append("(") 474 .append(utils.getTypeSignature(param.asType(), false, true)) 475 .append(")"); 476 } 477 blockTags.add(makeSeeTree(sb.toString(), setter)); 478 } 479 480 if (propMethod != member) { 481 String sig = "#" + propMethod.getSimpleName() + "()"; 482 blockTags.add(makeSeeTree(sig, propMethod)); 483 } 484 485 setDocCommentTree(member, fullBody, blockTags); 486 } 487 488 /** 489 * Creates a description that contains a reference to a state component of a record. 490 * The description is looked up as a resource, and should contain {@code {0}} where the 491 * reference to the component is to be inserted. The reference will be a link if the 492 * doc comment for the record has a {@code @param} tag for the component. 493 * @param key the resource key for the description 494 * @param elem the record element 495 * @param component the name of the component 496 * @return the description 497 */ 498 private List<DocTree> makeDescriptionWithComponent(String key, 499 TypeElement elem, Name component) { 500 List<DocTree> result = new ArrayList<>(); 501 String text = resources.getText(key); 502 int index = text.indexOf("{0}"); 503 result.add(treeFactory.newTextTree(text.substring(0, index))); 504 Name A = elementUtils.getName("a"); 505 Name CODE = elementUtils.getName("code"); 506 Name HREF = elementUtils.getName("href"); 507 List<DocTree> code = List.of( 508 treeFactory.newStartElementTree(CODE, List.of(), false), 509 treeFactory.newTextTree(component.toString()), 510 treeFactory.newEndElementTree(CODE)); 511 if (hasParamForComponent(elem, component)) { 512 DocTree href = treeFactory.newAttributeTree(HREF, 513 AttributeTree.ValueKind.DOUBLE, 514 List.of(treeFactory.newTextTree("#param-" + component))); 515 result 516 .add(treeFactory.newStartElementTree(A, List.of(href), false)); 517 result.addAll(code); 518 result.add(treeFactory.newEndElementTree(A)); 519 } else { 520 result.addAll(code); 521 } 522 result.add(treeFactory.newTextTree(text.substring(index + 3))); 523 return result; 524 } 525 526 /** 527 * Returns whether or not the doc comment for a record contains an {@code @param}} 528 * for a state component of the record. 529 * @param elem the record element 530 * @param component the name of the component 531 * @return whether or not there is a {@code @param}} for the component 532 */ 533 private boolean hasParamForComponent(TypeElement elem, Name component) { 534 DocCommentTree elemComment = utils.getDocCommentTree(elem); 535 if (elemComment == null) { 536 return false; 537 } 538 539 for (DocTree t : elemComment.getBlockTags()) { 540 if (t instanceof ParamTree pt 541 && pt.getName().getName() == component) { 542 return true; 543 } 544 } 545 546 return false; 547 } 548 549 /** 550 * Creates a description that contains the simple name of a program element 551 * The description is looked up as a resource, and should contain {@code {0}} where the 552 * name is to be inserted. 553 * @param key the resource key for the description 554 * @param name the name 555 * @return the description 556 */ 557 private List<DocTree> makeDescriptionWithName(String key, Name name) { 558 String text = resources.getText(key); 559 int index = text.indexOf("{0}"); 560 if (index == -1) { 561 return parse(text); 562 } else { 563 Name CODE = elementUtils.getName("code"); 564 var list = new ArrayList<DocTree>(); 565 list.addAll(parse(text.substring(0, index))); 566 list.add(treeFactory.newStartElementTree(CODE, List.of(), false)); 567 list.add(treeFactory.newTextTree(name.toString())); 568 list.add(treeFactory.newEndElementTree(CODE)); 569 list.addAll(parse(text.substring(index + 3))); 570 return list; 571 } 572 } 573 574 /** 575 * {@return a list containing the string for a given key in the doclet's 576 * resources, formatted with given arguments} 577 * 578 * @param key the key for the desired string 579 * @param o0 string or DocTree argument to be formatted into the result 580 */ 581 public List<? extends DocTree> getComment(String key, Object o0) { 582 return getComment(key, o0, null, null); 583 } 584 585 /** 586 * {@return a list containing the string for a given key in the doclet's 587 * resources, formatted with given arguments} 588 * 589 * @param key the key for the desired strings 590 * @param o0 string or a DocTree argument to be formatted into the result 591 * @param o1 string or a DocTree argument to be formatted into the result 592 * @param o2 string or a DocTree argument to be formatted into the result 593 */ 594 public List<? extends DocTree> getComment(String key, Object o0, Object o1, 595 Object o2) { 596 List<DocTree> l = new ArrayList<>(); 597 Pattern p = Pattern.compile("\\{([012])\\}"); 598 String text = resources.getText(key); 599 Matcher m = p.matcher(text); 600 int start = 0; 601 while (m.find(start)) { 602 l.add(makeTextTree(text.substring(start, m.start()))); 603 604 Object o = null; 605 switch (m.group(1).charAt(0)) { 606 case '0': 607 o = o0; 608 break; 609 case '1': 610 o = o1; 611 break; 612 case '2': 613 o = o2; 614 break; 615 } 616 617 if (o == null) { 618 l.add(makeTextTree("{" + m.group(1) + "}")); 619 } else if (o instanceof String str) { 620 l.add(makeTextTree(str)); 621 } else if (o instanceof DocTree t) { 622 l.add(t); 623 } 624 625 start = m.end(); 626 } 627 628 l.add(makeTextTree(text.substring(start))); 629 return l; 630 } 631 632 /* 633 * Returns the TreePath/DocCommentTree info that has been generated for an 634 * element. 635 * 636 * @param e the element 637 * 638 * @return the info object containing the tree path and doc comment 639 */ 640 // "synthetic" is not the best word here, and should not be confused with 641 // synthetic elements 642 public DocCommentInfo getSyntheticCommentInfo(Element e) { 643 return dcInfoMap.get(e); 644 } 645 646 /* 647 * Returns the TreePath/DocCommentTree info for HTML sources. 648 */ 649 public DocCommentInfo getHtmlCommentInfo(Element e) { 650 FileObject fo = null; 651 PackageElement pe = null; 652 switch (e.getKind()) { 653 case OTHER: 654 if (e instanceof DocletElement de) { 655 fo = de.getFileObject(); 656 pe = de.getPackageElement(); 657 } 658 break; 659 case PACKAGE: 660 pe = (PackageElement) e; 661 fo = configuration.workArounds.getJavaFileObject(pe); 662 break; 663 default: 664 return null; 665 } 666 if (fo == null) { 667 return null; 668 } 669 670 DocCommentTree dcTree = trees.getDocCommentTree(fo); 671 if (dcTree == null) { 672 return null; 673 } 674 DocTreePath treePath = trees.getDocTreePath(fo, pe); 675 return new DocCommentInfo(treePath.getTreePath(), dcTree); 676 } 677 678 public DocCommentTree parse(URI uri, String text) { 679 return trees.getDocCommentTree(new SimpleJavaFileObject( 680 uri, JavaFileObject.Kind.SOURCE) { 681 @Override 682 @DefinedBy(Api.COMPILER) 683 public CharSequence getCharContent(boolean ignoreEncoding) { 684 return text; 685 } 686 }); 687 } 688 689 public DocCommentInfo setDocCommentTree(Element element, 690 List<? extends DocTree> fullBody, 691 List<? extends DocTree> blockTags) { 692 DocCommentTree docTree 693 = treeFactory.newDocCommentTree(fullBody, blockTags); 694 return setDocCommentInfo(element, new DocCommentInfo(null, docTree)); 695 } 696 697 public DocCommentInfo setDocCommentInfo(Element element, 698 DocCommentInfo dci) { 699 DocCommentInfo prev = dcInfoMap.put(element, dci); 700 // A method having null comment (no comment) that might need to be 701 // replaced 702 // with a generated comment, remove such a comment from the cache. 703 utils.removeCommentHelper(element); 704 return prev; 705 } 706 707 /** 708 * Info about a doc comment: 709 * the position in the enclosing AST, and 710 * the parsed doc comment itself. 711 * 712 * The position in the AST is {@code null} for automatically generated comments, 713 * such as for {@code Enum.values}, {@code Enum.valuesOf}, and JavaFX properties. 714 */ 715 public static class DocCommentInfo { 716 /** 717 * The position of the comment in the enclosing AST, or {@code null} 718 * for automatically generated comments. 719 */ 720 public final TreePath treePath; 721 722 /** 723 * The doc comment tree that is the root node of a parsed doc comment, 724 * or {@code null} if there is no comment. 725 */ 726 public final DocCommentTree dcTree; 727 728 public DocCommentInfo(TreePath treePath, DocCommentTree dcTree) { 729 this.treePath = treePath; 730 this.dcTree = dcTree; 731 } 732 } 733}