1 /* 2 * Copyright (c) 2001, 2019, 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.taglets; 27 28 import java.io.*; 29 import java.util.*; 30 31 import javax.lang.model.element.Element; 32 import javax.lang.model.element.ExecutableElement; 33 import javax.lang.model.element.ModuleElement; 34 import javax.lang.model.element.PackageElement; 35 import javax.lang.model.element.TypeElement; 36 import javax.lang.model.element.VariableElement; 37 import javax.lang.model.util.SimpleElementVisitor14; 38 import javax.tools.JavaFileManager; 39 import javax.tools.StandardJavaFileManager; 40 41 import com.sun.source.doctree.DocTree; 42 import jdk.javadoc.doclet.Doclet; 43 import jdk.javadoc.doclet.DocletEnvironment; 44 import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration; 45 import jdk.javadoc.internal.doclets.toolkit.DocletElement; 46 import jdk.javadoc.internal.doclets.toolkit.Messages; 47 import jdk.javadoc.internal.doclets.toolkit.Resources; 48 49 import jdk.javadoc.internal.doclets.toolkit.taglets.BaseTaglet.Site; 50 import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper; 51 import jdk.javadoc.internal.doclets.toolkit.util.Utils; 52 53 import static javax.tools.DocumentationTool.Location.*; 54 55 import static com.sun.source.doctree.DocTree.Kind.*; 56 57 /** 58 * Manages the {@code Taglet}s used by doclets. 59 * 60 * <p><b>This is NOT part of any supported API. 61 * If you write code that depends on this, you do so at your own risk. 62 * This code and its internal interfaces are subject to change or 63 * deletion without notice.</b> 64 * 65 * @author Jamie Ho 66 */ 67 68 public class TagletManager { 69 70 /** 71 * The default separator for the simple tag option. 72 */ 73 public static final char SIMPLE_TAGLET_OPT_SEPARATOR = ':'; 74 75 /** 76 * The map of all taglets. 77 */ 78 private final LinkedHashMap<String,Taglet> allTaglets; 79 80 /** 81 * Block (non-line) taglets, grouped by Site 82 */ 83 private Map<Site, List<Taglet>> blockTagletsBySite; 84 85 /** 86 * The taglets that can appear inline in descriptive text. 87 */ 88 private List<Taglet> inlineTags; 89 90 /** 91 * The taglets that can appear in the serialized form. 92 */ 93 private List<Taglet> serializedFormTags; 94 95 private final DocletEnvironment docEnv; 96 private final Doclet doclet; 97 98 private final Utils utils; 99 private final Messages messages; 100 private final Resources resources; 101 102 /** 103 * Keep track of standard tags. 104 */ 105 private final Set<String> standardTags; 106 107 /** 108 * Keep track of standard tags in lowercase to compare for better 109 * error messages when a tag like @docRoot is mistakenly spelled 110 * lowercase @docroot. 111 */ 112 private final Set<String> standardTagsLowercase; 113 114 /** 115 * Keep track of overriden standard tags. 116 */ 117 private final Set<String> overridenStandardTags; 118 119 /** 120 * Keep track of the tags that may conflict 121 * with standard tags in the future (any custom tag without 122 * a period in its name). 123 */ 124 private final Set<String> potentiallyConflictingTags; 125 126 /** 127 * The set of unseen custom tags. 128 */ 129 private final Set<String> unseenCustomTags; 130 131 /** 132 * True if we do not want to use @since tags. 133 */ 134 private final boolean nosince; 135 136 /** 137 * True if we want to use @version tags. 138 */ 139 private final boolean showversion; 140 141 /** 142 * True if we want to use @author tags. 143 */ 144 private final boolean showauthor; 145 146 /** 147 * True if we want to use JavaFX-related tags (@defaultValue, @treatAsPrivate). 148 */ 149 private final boolean javafx; 150 151 /** 152 * Show the taglets table when it has been initialized. 153 */ 154 private final boolean showTaglets; 155 156 /** 157 * Construct a new {@code TagletManager}. 158 * @param nosince true if we do not want to use @since tags. 159 * @param showversion true if we want to use @version tags. 160 * @param showauthor true if we want to use @author tags. 161 * @param javafx indicates whether javafx is active. 162 * @param configuration the configuration for this taglet manager 163 */ 164 public TagletManager(boolean nosince, boolean showversion, 165 boolean showauthor, boolean javafx, 166 BaseConfiguration configuration) { 167 overridenStandardTags = new HashSet<>(); 168 potentiallyConflictingTags = new HashSet<>(); 169 standardTags = new HashSet<>(); 170 standardTagsLowercase = new HashSet<>(); 171 unseenCustomTags = new HashSet<>(); 172 allTaglets = new LinkedHashMap<>(); 173 this.nosince = nosince; 174 this.showversion = showversion; 175 this.showauthor = showauthor; 176 this.javafx = javafx; 177 this.docEnv = configuration.docEnv; 178 this.doclet = configuration.doclet; 179 this.messages = configuration.getMessages(); 180 this.resources = configuration.getResources(); 181 this.showTaglets = configuration.showTaglets; 182 this.utils = configuration.utils; 183 initStandardTaglets(); 184 } 185 186 /** 187 * Add a new {@code Taglet}. This is used to add a Taglet from within 188 * a Doclet. No message is printed to indicate that the Taglet is properly 189 * registered because these Taglets are typically added for every execution of the 190 * Doclet. We don't want to see this type of error message every time. 191 * @param customTag the new {@code Taglet} to add. 192 */ 193 public void addCustomTag(Taglet customTag) { 194 if (customTag != null) { 195 String name = customTag.getName(); 196 allTaglets.remove(name); 197 allTaglets.put(name, customTag); 198 checkTagName(name); 199 } 200 } 201 202 public Set<String> getAllTagletNames() { 203 return allTaglets.keySet(); 204 } 205 206 /** 207 * Initializes the location TAGLET_PATH which is used to locate the custom taglets. 208 * @param fileManager the filemanager to load classes and resources. 209 * @param tagletPath the path to the custom taglet. 210 * @throws IOException if an error occurs while setting the location. 211 */ 212 public void initTagletPath(JavaFileManager fileManager, String tagletPath) throws IOException { 213 if (fileManager instanceof StandardJavaFileManager) { 214 StandardJavaFileManager sfm = (StandardJavaFileManager)fileManager; 215 if (tagletPath != null) { 216 List<File> paths = new ArrayList<>(); 217 for (String pathname : tagletPath.split(File.pathSeparator)) { 218 paths.add(new File(pathname)); 219 } 220 sfm.setLocation(TAGLET_PATH, paths); 221 } else if (!sfm.hasLocation(TAGLET_PATH)) { 222 sfm.setLocation(TAGLET_PATH, Collections.emptyList()); 223 } 224 } else if (tagletPath != null) { 225 messages.error("doclet.not_standard_file_manager"); 226 } 227 } 228 229 /** 230 * Adds a new {@code Taglet}. Print a message to indicate whether or not 231 * the Taglet was registered properly. 232 * @param classname the name of the class representing the custom tag. 233 * @param fileManager the filemanager to load classes and resources. 234 */ 235 public void addCustomTag(String classname, JavaFileManager fileManager) { 236 try { 237 ClassLoader tagClassLoader; 238 tagClassLoader = fileManager.getClassLoader(TAGLET_PATH); 239 Class<? extends jdk.javadoc.doclet.Taglet> customTagClass = 240 tagClassLoader.loadClass(classname).asSubclass(jdk.javadoc.doclet.Taglet.class); 241 jdk.javadoc.doclet.Taglet instance = customTagClass.getConstructor().newInstance(); 242 registerTaglet(instance); 243 } catch (ReflectiveOperationException exc) { 244 messages.error("doclet.Error_taglet_not_registered", exc.getClass().getName(), 245 classname); 246 } 247 } 248 249 /** 250 * Loads taglets from a taglet path using service loader. 251 * @param fileManager the filemanager to load the taglets. 252 * @throws IOException if an error occurs while getting the service loader. 253 */ 254 public void loadTaglets(JavaFileManager fileManager) throws IOException { 255 Iterable<? extends File> location = ((StandardJavaFileManager)fileManager).getLocation(TAGLET_PATH); 256 if (location != null && location.iterator().hasNext()) { 257 ServiceLoader<jdk.javadoc.doclet.Taglet> serviceLoader = 258 fileManager.getServiceLoader(TAGLET_PATH, jdk.javadoc.doclet.Taglet.class); 259 Iterator<jdk.javadoc.doclet.Taglet> iterator = serviceLoader.iterator(); 260 while (iterator.hasNext()) { 261 jdk.javadoc.doclet.Taglet taglet = iterator.next(); 262 registerTaglet(taglet); 263 } 264 } 265 } 266 267 /** 268 * Registers the {@code Taglet}. Prints a message if a {@code Taglet} got registered properly. 269 * @param instance the {@code Taglet} instance. 270 */ 271 private void registerTaglet(jdk.javadoc.doclet.Taglet instance) { 272 instance.init(docEnv, doclet); 273 Taglet newLegacy = new UserTaglet(instance); 274 allTaglets.put(newLegacy.getName(), newLegacy); 275 messages.notice("doclet.Notice_taglet_registered", instance.getClass().getName()); 276 } 277 278 /** 279 * Add a new {@code SimpleTaglet}. If this tag already exists 280 * and the header passed as an argument is null, move tag to the back of the 281 * list. If this tag already exists and the header passed as an argument is 282 * not null, overwrite previous tag with new one. Otherwise, add new 283 * SimpleTaglet to list. 284 * @param tagName the name of this tag 285 * @param header the header to output. 286 * @param locations the possible locations that this tag 287 * can appear in. 288 */ 289 public void addNewSimpleCustomTag(String tagName, String header, String locations) { 290 if (tagName == null || locations == null) { 291 return; 292 } 293 Taglet tag = allTaglets.get(tagName); 294 if (tag == null || header != null) { 295 allTaglets.remove(tagName); 296 allTaglets.put(tagName, new SimpleTaglet(tagName, header, locations)); 297 if (Utils.toLowerCase(locations).indexOf('x') == -1) { 298 checkTagName(tagName); 299 } 300 } else { 301 //Move to back 302 allTaglets.remove(tagName); 303 allTaglets.put(tagName, tag); 304 } 305 } 306 307 /** 308 * Given a tag name, add it to the set of tags it belongs to. 309 */ 310 private void checkTagName(String name) { 311 if (standardTags.contains(name)) { 312 overridenStandardTags.add(name); 313 } else { 314 if (name.indexOf('.') == -1) { 315 potentiallyConflictingTags.add(name); 316 } 317 unseenCustomTags.add(name); 318 } 319 } 320 321 /** 322 * Given a name of a seen custom tag, remove it from the set of unseen 323 * custom tags. 324 * @param name the name of the seen custom tag. 325 */ 326 void seenCustomTag(String name) { 327 unseenCustomTags.remove(name); 328 } 329 330 /** 331 * Given a series of {@code DocTree}s, check for spelling mistakes. 332 * @param element the tags holder 333 * @param trees the trees containing the comments 334 * @param areInlineTags true if the array of tags are inline and false otherwise. 335 */ 336 @SuppressWarnings("preview") 337 public void checkTags(Element element, Iterable<? extends DocTree> trees, boolean areInlineTags) { 338 if (trees == null) { 339 return; 340 } 341 CommentHelper ch = utils.getCommentHelper(element); 342 for (DocTree tag : trees) { 343 String name = tag.getKind().tagName; 344 if (name == null) { 345 continue; 346 } 347 if (name.length() > 0 && name.charAt(0) == '@') { 348 name = name.substring(1, name.length()); 349 } 350 if (! (standardTags.contains(name) || allTaglets.containsKey(name))) { 351 if (standardTagsLowercase.contains(Utils.toLowerCase(name))) { 352 messages.warning(ch.getDocTreePath(tag), "doclet.UnknownTagLowercase", ch.getTagName(tag)); 353 continue; 354 } else { 355 messages.warning(ch.getDocTreePath(tag), "doclet.UnknownTag", ch.getTagName(tag)); 356 continue; 357 } 358 } 359 final Taglet taglet = allTaglets.get(name); 360 // Check and verify tag usage 361 if (taglet != null) { 362 if (areInlineTags && !taglet.isInlineTag()) { 363 printTagMisuseWarn(ch, taglet, tag, "inline"); 364 } 365 // nothing more to do 366 if (element == null) { 367 return; 368 } 369 new SimpleElementVisitor14<Void, Void>() { 370 @Override 371 public Void visitModule(ModuleElement e, Void p) { 372 if (!taglet.inModule()) { 373 printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "module"); 374 } 375 return null; 376 } 377 378 @Override 379 public Void visitPackage(PackageElement e, Void p) { 380 if (!taglet.inPackage()) { 381 printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "package"); 382 } 383 return null; 384 } 385 386 @Override 387 public Void visitType(TypeElement e, Void p) { 388 if (!taglet.inType()) { 389 printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "class"); 390 } 391 return null; 392 } 393 394 @Override 395 public Void visitExecutable(ExecutableElement e, Void p) { 396 if (utils.isConstructor(e) && !taglet.inConstructor()) { 397 printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "constructor"); 398 } else if (!taglet.inMethod()) { 399 printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "method"); 400 } 401 return null; 402 } 403 404 @Override 405 public Void visitVariable(VariableElement e, Void p) { 406 if (utils.isField(e) && !taglet.inField()) { 407 printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "field"); 408 } 409 return null; 410 } 411 412 @Override 413 public Void visitUnknown(Element e, Void p) { 414 if (utils.isOverviewElement(e) && !taglet.inOverview()) { 415 printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "overview"); 416 } 417 return null; 418 } 419 420 @Override 421 protected Void defaultAction(Element e, Void p) { 422 return null; 423 } 424 }.visit(element); 425 } 426 } 427 } 428 429 /** 430 * Given the taglet, the tag and the type of documentation that the tag 431 * was found in, print a tag misuse warning. 432 * @param taglet the taglet representing the misused tag. 433 * @param tag the misused tag. 434 * @param holderType the type of documentation that the misused tag was found in. 435 */ 436 private void printTagMisuseWarn(CommentHelper ch, Taglet taglet, DocTree tag, String holderType) { 437 Set<String> locationsSet = new LinkedHashSet<>(); 438 // The following names should be localized 439 if (taglet.inOverview()) { 440 locationsSet.add("overview"); 441 } 442 if (taglet.inModule()) { 443 locationsSet.add("module"); 444 } 445 if (taglet.inPackage()) { 446 locationsSet.add("package"); 447 } 448 if (taglet.inType()) { 449 locationsSet.add("class/interface"); 450 } 451 if (taglet.inConstructor()) { 452 locationsSet.add("constructor"); 453 } 454 if (taglet.inField()) { 455 locationsSet.add("field"); 456 } 457 if (taglet.inMethod()) { 458 locationsSet.add("method"); 459 } 460 if (taglet.isInlineTag()) { 461 locationsSet.add("inline text"); 462 } 463 if (locationsSet.isEmpty()) { 464 //This known tag is excluded. 465 return; 466 } 467 StringBuilder combined_locations = new StringBuilder(); 468 for (String location: locationsSet) { 469 if (combined_locations.length() > 0) { 470 combined_locations.append(", "); 471 } 472 combined_locations.append(location); 473 } 474 messages.warning(ch.getDocTreePath(tag), "doclet.tag_misuse", 475 "@" + taglet.getName(), holderType, combined_locations.toString()); 476 } 477 478 /** 479 * Returns the taglets that can appear inline, in descriptive text. 480 * @return the taglets that can appear inline 481 */ 482 List<Taglet> getInlineTaglets() { 483 if (inlineTags == null) { 484 initBlockTaglets(); 485 } 486 return inlineTags; 487 } 488 489 /** 490 * Returns the taglets that can appear in the serialized form. 491 * @return the taglet that can appear in the serialized form 492 */ 493 public List<Taglet> getSerializedFormTaglets() { 494 if (serializedFormTags == null) { 495 initBlockTaglets(); 496 } 497 return serializedFormTags; 498 } 499 500 /** 501 * Returns the custom tags for a given element. 502 * 503 * @param e the element to get custom tags for 504 * @return the array of {@code Taglet}s that can 505 * appear in the given element. 506 */ 507 @SuppressWarnings("fallthrough") 508 public List<Taglet> getBlockTaglets(Element e) { 509 if (blockTagletsBySite == null) { 510 initBlockTaglets(); 511 } 512 513 switch (e.getKind()) { 514 case CONSTRUCTOR: 515 return blockTagletsBySite.get(Site.CONSTRUCTOR); 516 case METHOD: 517 return blockTagletsBySite.get(Site.METHOD); 518 case ENUM_CONSTANT: 519 case FIELD: 520 return blockTagletsBySite.get(Site.FIELD); 521 case ANNOTATION_TYPE: 522 case INTERFACE: 523 case CLASS: 524 case ENUM: 525 case RECORD: 526 return blockTagletsBySite.get(Site.TYPE); 527 case MODULE: 528 return blockTagletsBySite.get(Site.MODULE); 529 case PACKAGE: 530 return blockTagletsBySite.get(Site.PACKAGE); 531 case OTHER: 532 if (e instanceof DocletElement) { 533 DocletElement de = (DocletElement)e; 534 switch (de.getSubKind()) { 535 case DOCFILE: 536 return blockTagletsBySite.get(Site.PACKAGE); 537 case OVERVIEW: 538 return blockTagletsBySite.get(Site.OVERVIEW); 539 default: 540 // fall through 541 } 542 } 543 // fall through 544 default: 545 throw new AssertionError("unknown element: " + e + " ,kind: " + e.getKind()); 546 } 547 } 548 549 /** 550 * Initialize the custom tag Lists. 551 */ 552 private void initBlockTaglets() { 553 554 blockTagletsBySite = new EnumMap<>(Site.class); 555 for (Site site : Site.values()) { 556 blockTagletsBySite.put(site, new ArrayList<>()); 557 } 558 559 inlineTags = new ArrayList<>(); 560 561 for (Taglet current : allTaglets.values()) { 562 if (current.isInlineTag()) { 563 inlineTags.add(current); 564 } else { 565 if (current.inOverview()) { 566 blockTagletsBySite.get(Site.OVERVIEW).add(current); 567 } 568 if (current.inModule()) { 569 blockTagletsBySite.get(Site.MODULE).add(current); 570 } 571 if (current.inPackage()) { 572 blockTagletsBySite.get(Site.PACKAGE).add(current); 573 } 574 if (current.inType()) { 575 blockTagletsBySite.get(Site.TYPE).add(current); 576 } 577 if (current.inConstructor()) { 578 blockTagletsBySite.get(Site.CONSTRUCTOR).add(current); 579 } 580 if (current.inMethod()) { 581 blockTagletsBySite.get(Site.METHOD).add(current); 582 } 583 if (current.inField()) { 584 blockTagletsBySite.get(Site.FIELD).add(current); 585 } 586 } 587 } 588 589 //Init the serialized form tags 590 serializedFormTags = new ArrayList<>(); 591 serializedFormTags.add(allTaglets.get(SERIAL_DATA.tagName)); 592 serializedFormTags.add(allTaglets.get(THROWS.tagName)); 593 if (!nosince) 594 serializedFormTags.add(allTaglets.get(SINCE.tagName)); 595 serializedFormTags.add(allTaglets.get(SEE.tagName)); 596 597 if (showTaglets) { 598 showTaglets(System.out); 599 } 600 } 601 602 /** 603 * Initialize standard Javadoc tags for ordering purposes. 604 */ 605 private void initStandardTaglets() { 606 if (javafx) { 607 initJavaFXTaglets(); 608 } 609 610 addStandardTaglet(new ParamTaglet()); 611 addStandardTaglet(new ReturnTaglet()); 612 addStandardTaglet(new ThrowsTaglet()); 613 addStandardTaglet( 614 new SimpleTaglet(EXCEPTION.tagName, null, 615 EnumSet.of(Site.METHOD, Site.CONSTRUCTOR))); 616 addStandardTaglet( 617 new SimpleTaglet(SINCE.tagName, resources.getText("doclet.Since"), 618 EnumSet.allOf(Site.class), !nosince)); 619 addStandardTaglet( 620 new SimpleTaglet(VERSION.tagName, resources.getText("doclet.Version"), 621 EnumSet.of(Site.OVERVIEW, Site.MODULE, Site.PACKAGE, Site.TYPE), showversion)); 622 addStandardTaglet( 623 new SimpleTaglet(AUTHOR.tagName, resources.getText("doclet.Author"), 624 EnumSet.of(Site.OVERVIEW, Site.MODULE, Site.PACKAGE, Site.TYPE), showauthor)); 625 addStandardTaglet( 626 new SimpleTaglet(SERIAL_DATA.tagName, resources.getText("doclet.SerialData"), 627 EnumSet.noneOf(Site.class))); 628 addStandardTaglet( 629 new SimpleTaglet(HIDDEN.tagName, null, 630 EnumSet.of(Site.TYPE, Site.METHOD, Site.FIELD))); 631 632 // This appears to be a default custom (non-standard) taglet 633 Taglet factoryTaglet = new SimpleTaglet("factory", resources.getText("doclet.Factory"), 634 EnumSet.of(Site.METHOD)); 635 allTaglets.put(factoryTaglet.getName(), factoryTaglet); 636 637 addStandardTaglet(new SeeTaglet()); 638 639 // Standard inline tags 640 addStandardTaglet(new DocRootTaglet()); 641 addStandardTaglet(new InheritDocTaglet()); 642 addStandardTaglet(new ValueTaglet()); 643 addStandardTaglet(new LiteralTaglet()); 644 addStandardTaglet(new CodeTaglet()); 645 addStandardTaglet(new IndexTaglet()); 646 addStandardTaglet(new SummaryTaglet()); 647 addStandardTaglet(new SystemPropertyTaglet()); 648 649 // Keep track of the names of standard tags for error checking purposes. 650 // The following are not handled above. 651 addStandardTaglet(new DeprecatedTaglet()); 652 addStandardTaglet(new BaseTaglet(LINK.tagName, true, EnumSet.allOf(Site.class))); 653 addStandardTaglet(new BaseTaglet(LINK_PLAIN.tagName, true, EnumSet.allOf(Site.class))); 654 addStandardTaglet(new BaseTaglet(USES.tagName, false, EnumSet.of(Site.MODULE))); 655 addStandardTaglet(new BaseTaglet(PROVIDES.tagName, false, EnumSet.of(Site.MODULE))); 656 addStandardTaglet( 657 new SimpleTaglet(SERIAL.tagName, null, 658 EnumSet.of(Site.PACKAGE, Site.TYPE, Site.FIELD))); 659 addStandardTaglet( 660 new SimpleTaglet(SERIAL_FIELD.tagName, null, EnumSet.of(Site.FIELD))); 661 } 662 663 /** 664 * Initialize JavaFX-related tags. 665 */ 666 private void initJavaFXTaglets() { 667 addStandardTaglet(new PropertyGetterTaglet()); 668 addStandardTaglet(new PropertySetterTaglet()); 669 addStandardTaglet(new SimpleTaglet("propertyDescription", 670 resources.getText("doclet.PropertyDescription"), 671 EnumSet.of(Site.METHOD, Site.FIELD))); 672 addStandardTaglet(new SimpleTaglet("defaultValue", resources.getText("doclet.DefaultValue"), 673 EnumSet.of(Site.METHOD, Site.FIELD))); 674 addStandardTaglet(new SimpleTaglet("treatAsPrivate", null, 675 EnumSet.of(Site.TYPE, Site.METHOD, Site.FIELD))); 676 } 677 678 private void addStandardTaglet(Taglet taglet) { 679 String name = taglet.getName(); 680 allTaglets.put(name, taglet); 681 standardTags.add(name); 682 standardTagsLowercase.add(Utils.toLowerCase(name)); 683 } 684 685 public boolean isKnownCustomTag(String tagName) { 686 return allTaglets.containsKey(tagName); 687 } 688 689 /** 690 * Print a list of {@link Taglet}s that might conflict with 691 * standard tags in the future and a list of standard tags 692 * that have been overriden. 693 */ 694 public void printReport() { 695 printReportHelper("doclet.Notice_taglet_conflict_warn", potentiallyConflictingTags); 696 printReportHelper("doclet.Notice_taglet_overriden", overridenStandardTags); 697 printReportHelper("doclet.Notice_taglet_unseen", unseenCustomTags); 698 } 699 700 private void printReportHelper(String noticeKey, Set<String> names) { 701 if (names.size() > 0) { 702 StringBuilder result = new StringBuilder(); 703 for (String name : names) { 704 result.append(result.length() == 0 ? " " : ", "); 705 result.append("@").append(name); 706 } 707 messages.notice(noticeKey, result); 708 } 709 } 710 711 /** 712 * Given the name of a tag, return the corresponding taglet. 713 * Return null if the tag is unknown. 714 * 715 * @param name the name of the taglet to retrieve. 716 * @return return the corresponding taglet. Return null if the tag is 717 * unknown. 718 */ 719 Taglet getTaglet(String name) { 720 if (name.indexOf("@") == 0) { 721 return allTaglets.get(name.substring(1)); 722 } else { 723 return allTaglets.get(name); 724 } 725 726 } 727 728 /* 729 * The output of this method is the basis for a table at the end of the 730 * doc comment specification, so any changes in the output may indicate 731 * a need for a corresponding update to the spec. 732 */ 733 private void showTaglets(PrintStream out) { 734 Set<Taglet> taglets = new TreeSet<>((o1, o2) -> o1.getName().compareTo(o2.getName())); 735 taglets.addAll(allTaglets.values()); 736 737 for (Taglet t : taglets) { 738 String name = t.isInlineTag() ? "{@" + t.getName() + "}" : "@" + t.getName(); 739 out.println(String.format("%20s", name) + ": " 740 + format(t.inOverview(), "overview") + " " 741 + format(t.inModule(), "module") + " " 742 + format(t.inPackage(), "package") + " " 743 + format(t.inType(), "type") + " " 744 + format(t.inConstructor(),"constructor") + " " 745 + format(t.inMethod(), "method") + " " 746 + format(t.inField(), "field") + " " 747 + format(t.isInlineTag(), "inline")+ " " 748 + format((t instanceof SimpleTaglet) && !((SimpleTaglet)t).enabled, "disabled")); 749 } 750 } 751 752 private String format(boolean b, String s) { 753 return b ? s : s.replaceAll(".", "."); // replace all with "." 754 } 755 }