1 /* 2 * Copyright (c) 2001, 2018, 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.SimpleElementVisitor9; 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 public void checkTags(Element element, Iterable<? extends DocTree> trees, boolean areInlineTags) { 337 if (trees == null) { 338 return; 339 } 340 CommentHelper ch = utils.getCommentHelper(element); 341 for (DocTree tag : trees) { 342 String name = tag.getKind().tagName; 343 if (name == null) { 344 continue; 345 } 346 if (name.length() > 0 && name.charAt(0) == '@') { 347 name = name.substring(1, name.length()); 348 } 349 if (! (standardTags.contains(name) || allTaglets.containsKey(name))) { 350 if (standardTagsLowercase.contains(Utils.toLowerCase(name))) { 351 messages.warning(ch.getDocTreePath(tag), "doclet.UnknownTagLowercase", ch.getTagName(tag)); 352 continue; 353 } else { 354 messages.warning(ch.getDocTreePath(tag), "doclet.UnknownTag", ch.getTagName(tag)); 355 continue; 356 } 357 } 358 final Taglet taglet = allTaglets.get(name); 359 // Check and verify tag usage 360 if (taglet != null) { 361 if (areInlineTags && !taglet.isInlineTag()) { 362 printTagMisuseWarn(ch, taglet, tag, "inline"); 363 } 364 // nothing more to do 365 if (element == null) { 366 return; 367 } 368 new SimpleElementVisitor9<Void, Void>() { 369 @Override 370 public Void visitModule(ModuleElement e, Void p) { 371 if (!taglet.inModule()) { 372 printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "module"); 373 } 374 return null; 375 } 376 377 @Override 378 public Void visitPackage(PackageElement e, Void p) { 379 if (!taglet.inPackage()) { 380 printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "package"); 381 } 382 return null; 383 } 384 385 @Override 386 public Void visitType(TypeElement e, Void p) { 387 if (!taglet.inType()) { 388 printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "class"); 389 } 390 return null; 391 } 392 393 @Override 394 public Void visitExecutable(ExecutableElement e, Void p) { 395 if (utils.isConstructor(e) && !taglet.inConstructor()) { 396 printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "constructor"); 397 } else if (!taglet.inMethod()) { 398 printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "method"); 399 } 400 return null; 401 } 402 403 @Override 404 public Void visitVariable(VariableElement e, Void p) { 405 if (utils.isField(e) && !taglet.inField()) { 406 printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "field"); 407 } 408 return null; 409 } 410 411 @Override 412 public Void visitUnknown(Element e, Void p) { 413 if (utils.isOverviewElement(e) && !taglet.inOverview()) { 414 printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "overview"); 415 } 416 return null; 417 } 418 419 @Override 420 protected Void defaultAction(Element e, Void p) { 421 return null; 422 } 423 }.visit(element); 424 } 425 } 426 } 427 428 /** 429 * Given the taglet, the tag and the type of documentation that the tag 430 * was found in, print a tag misuse warning. 431 * @param taglet the taglet representing the misused tag. 432 * @param tag the misused tag. 433 * @param holderType the type of documentation that the misused tag was found in. 434 */ 435 private void printTagMisuseWarn(CommentHelper ch, Taglet taglet, DocTree tag, String holderType) { 436 Set<String> locationsSet = new LinkedHashSet<>(); 437 // The following names should be localized 438 if (taglet.inOverview()) { 439 locationsSet.add("overview"); 440 } 441 if (taglet.inModule()) { 442 locationsSet.add("module"); 443 } 444 if (taglet.inPackage()) { 445 locationsSet.add("package"); 446 } 447 if (taglet.inType()) { 448 locationsSet.add("class/interface"); 449 } 450 if (taglet.inConstructor()) { 451 locationsSet.add("constructor"); 452 } 453 if (taglet.inField()) { 454 locationsSet.add("field"); 455 } 456 if (taglet.inMethod()) { 457 locationsSet.add("method"); 458 } 459 if (taglet.isInlineTag()) { 460 locationsSet.add("inline text"); 461 } 462 if (locationsSet.isEmpty()) { 463 //This known tag is excluded. 464 return; 465 } 466 StringBuilder combined_locations = new StringBuilder(); 467 for (String location: locationsSet) { 468 if (combined_locations.length() > 0) { 469 combined_locations.append(", "); 470 } 471 combined_locations.append(location); 472 } 473 messages.warning(ch.getDocTreePath(tag), "doclet.tag_misuse", 474 "@" + taglet.getName(), holderType, combined_locations.toString()); 475 } 476 477 /** 478 * Returns the taglets that can appear inline, in descriptive text. 479 * @return the taglets that can appear inline 480 */ 481 List<Taglet> getInlineTaglets() { 482 if (inlineTags == null) { 483 initBlockTaglets(); 484 } 485 return inlineTags; 486 } 487 488 /** 489 * Returns the taglets that can appear in the serialized form. 490 * @return the taglet that can appear in the serialized form 491 */ 492 public List<Taglet> getSerializedFormTaglets() { 493 if (serializedFormTags == null) { 494 initBlockTaglets(); 495 } 496 return serializedFormTags; 497 } 498 499 /** 500 * Returns the custom tags for a given element. 501 * 502 * @param e the element to get custom tags for 503 * @return the array of {@code Taglet}s that can 504 * appear in the given element. 505 */ 506 @SuppressWarnings("fallthrough") 507 public List<Taglet> getBlockTaglets(Element e) { 508 if (blockTagletsBySite == null) { 509 initBlockTaglets(); 510 } 511 512 switch (e.getKind()) { 513 case CONSTRUCTOR: 514 return blockTagletsBySite.get(Site.CONSTRUCTOR); 515 case METHOD: 516 return blockTagletsBySite.get(Site.METHOD); 517 case ENUM_CONSTANT: 518 case FIELD: 519 return blockTagletsBySite.get(Site.FIELD); 520 case ANNOTATION_TYPE: 521 case INTERFACE: 522 case CLASS: 523 case ENUM: 524 return blockTagletsBySite.get(Site.TYPE); 525 case MODULE: 526 return blockTagletsBySite.get(Site.MODULE); 527 case PACKAGE: 528 return blockTagletsBySite.get(Site.PACKAGE); 529 case OTHER: 530 if (e instanceof DocletElement) { 531 DocletElement de = (DocletElement)e; 532 switch (de.getSubKind()) { 533 case DOCFILE: 534 return blockTagletsBySite.get(Site.PACKAGE); 535 case OVERVIEW: 536 return blockTagletsBySite.get(Site.OVERVIEW); 537 default: 538 // fall through 539 } 540 } 541 // fall through 542 default: 543 throw new AssertionError("unknown element: " + e + " ,kind: " + e.getKind()); 544 } 545 } 546 547 /** 548 * Initialize the custom tag Lists. 549 */ 550 private void initBlockTaglets() { 551 552 blockTagletsBySite = new EnumMap<>(Site.class); 553 for (Site site : Site.values()) { 554 blockTagletsBySite.put(site, new ArrayList<>()); 555 } 556 557 inlineTags = new ArrayList<>(); 558 559 for (Taglet current : allTaglets.values()) { 560 if (current.isInlineTag()) { 561 inlineTags.add(current); 562 } else { 563 if (current.inOverview()) { 564 blockTagletsBySite.get(Site.OVERVIEW).add(current); 565 } 566 if (current.inModule()) { 567 blockTagletsBySite.get(Site.MODULE).add(current); 568 } 569 if (current.inPackage()) { 570 blockTagletsBySite.get(Site.PACKAGE).add(current); 571 } 572 if (current.inType()) { 573 blockTagletsBySite.get(Site.TYPE).add(current); 574 } 575 if (current.inConstructor()) { 576 blockTagletsBySite.get(Site.CONSTRUCTOR).add(current); 577 } 578 if (current.inMethod()) { 579 blockTagletsBySite.get(Site.METHOD).add(current); 580 } 581 if (current.inField()) { 582 blockTagletsBySite.get(Site.FIELD).add(current); 583 } 584 } 585 } 586 587 //Init the serialized form tags 588 serializedFormTags = new ArrayList<>(); 589 serializedFormTags.add(allTaglets.get(SERIAL_DATA.tagName)); 590 serializedFormTags.add(allTaglets.get(THROWS.tagName)); 591 if (!nosince) 592 serializedFormTags.add(allTaglets.get(SINCE.tagName)); 593 serializedFormTags.add(allTaglets.get(SEE.tagName)); 594 595 if (showTaglets) { 596 showTaglets(System.out); 597 } 598 } 599 600 /** 601 * Initialize standard Javadoc tags for ordering purposes. 602 */ 603 private void initStandardTaglets() { 604 if (javafx) { 605 initJavaFXTaglets(); 606 } 607 608 addStandardTaglet(new ParamTaglet()); 609 addStandardTaglet(new ReturnTaglet()); 610 addStandardTaglet(new ThrowsTaglet()); 611 addStandardTaglet( 612 new SimpleTaglet(EXCEPTION.tagName, null, 613 EnumSet.of(Site.METHOD, Site.CONSTRUCTOR))); 614 addStandardTaglet( 615 new SimpleTaglet(SINCE.tagName, resources.getText("doclet.Since"), 616 EnumSet.allOf(Site.class), !nosince)); 617 addStandardTaglet( 618 new SimpleTaglet(VERSION.tagName, resources.getText("doclet.Version"), 619 EnumSet.of(Site.OVERVIEW, Site.MODULE, Site.PACKAGE, Site.TYPE), showversion)); 620 addStandardTaglet( 621 new SimpleTaglet(AUTHOR.tagName, resources.getText("doclet.Author"), 622 EnumSet.of(Site.OVERVIEW, Site.MODULE, Site.PACKAGE, Site.TYPE), showauthor)); 623 addStandardTaglet( 624 new SimpleTaglet(SERIAL_DATA.tagName, resources.getText("doclet.SerialData"), 625 EnumSet.noneOf(Site.class))); 626 addStandardTaglet( 627 new SimpleTaglet(HIDDEN.tagName, null, 628 EnumSet.of(Site.TYPE, Site.METHOD, Site.FIELD))); 629 630 // This appears to be a default custom (non-standard) taglet 631 Taglet factoryTaglet = new SimpleTaglet("factory", resources.getText("doclet.Factory"), 632 EnumSet.of(Site.METHOD)); 633 allTaglets.put(factoryTaglet.getName(), factoryTaglet); 634 635 addStandardTaglet(new SeeTaglet()); 636 637 // Standard inline tags 638 addStandardTaglet(new DocRootTaglet()); 639 addStandardTaglet(new InheritDocTaglet()); 640 addStandardTaglet(new ValueTaglet()); 641 addStandardTaglet(new LiteralTaglet()); 642 addStandardTaglet(new CodeTaglet()); 643 addStandardTaglet(new IndexTaglet()); 644 addStandardTaglet(new SummaryTaglet()); 645 addStandardTaglet(new SystemPropertyTaglet()); 646 647 // Keep track of the names of standard tags for error checking purposes. 648 // The following are not handled above. 649 addStandardTaglet(new DeprecatedTaglet()); 650 addStandardTaglet(new BaseTaglet(LINK.tagName, true, EnumSet.allOf(Site.class))); 651 addStandardTaglet(new BaseTaglet(LINK_PLAIN.tagName, true, EnumSet.allOf(Site.class))); 652 addStandardTaglet(new BaseTaglet(USES.tagName, false, EnumSet.of(Site.MODULE))); 653 addStandardTaglet(new BaseTaglet(PROVIDES.tagName, false, EnumSet.of(Site.MODULE))); 654 addStandardTaglet( 655 new SimpleTaglet(SERIAL.tagName, null, 656 EnumSet.of(Site.PACKAGE, Site.TYPE, Site.FIELD))); 657 addStandardTaglet( 658 new SimpleTaglet(SERIAL_FIELD.tagName, null, EnumSet.of(Site.FIELD))); 659 } 660 661 /** 662 * Initialize JavaFX-related tags. 663 */ 664 private void initJavaFXTaglets() { 665 addStandardTaglet(new PropertyGetterTaglet()); 666 addStandardTaglet(new PropertySetterTaglet()); 667 addStandardTaglet(new SimpleTaglet("propertyDescription", 668 resources.getText("doclet.PropertyDescription"), 669 EnumSet.of(Site.METHOD, Site.FIELD))); 670 addStandardTaglet(new SimpleTaglet("defaultValue", resources.getText("doclet.DefaultValue"), 671 EnumSet.of(Site.METHOD, Site.FIELD))); 672 addStandardTaglet(new SimpleTaglet("treatAsPrivate", null, 673 EnumSet.of(Site.TYPE, Site.METHOD, Site.FIELD))); 674 } 675 676 private void addStandardTaglet(Taglet taglet) { 677 String name = taglet.getName(); 678 allTaglets.put(name, taglet); 679 standardTags.add(name); 680 standardTagsLowercase.add(Utils.toLowerCase(name)); 681 } 682 683 public boolean isKnownCustomTag(String tagName) { 684 return allTaglets.containsKey(tagName); 685 } 686 687 /** 688 * Print a list of {@link Taglet}s that might conflict with 689 * standard tags in the future and a list of standard tags 690 * that have been overriden. 691 */ 692 public void printReport() { 693 printReportHelper("doclet.Notice_taglet_conflict_warn", potentiallyConflictingTags); 694 printReportHelper("doclet.Notice_taglet_overriden", overridenStandardTags); 695 printReportHelper("doclet.Notice_taglet_unseen", unseenCustomTags); 696 } 697 698 private void printReportHelper(String noticeKey, Set<String> names) { 699 if (names.size() > 0) { 700 StringBuilder result = new StringBuilder(); 701 for (String name : names) { 702 result.append(result.length() == 0 ? " " : ", "); 703 result.append("@").append(name); 704 } 705 messages.notice(noticeKey, result); 706 } 707 } 708 709 /** 710 * Given the name of a tag, return the corresponding taglet. 711 * Return null if the tag is unknown. 712 * 713 * @param name the name of the taglet to retrieve. 714 * @return return the corresponding taglet. Return null if the tag is 715 * unknown. 716 */ 717 Taglet getTaglet(String name) { 718 if (name.indexOf("@") == 0) { 719 return allTaglets.get(name.substring(1)); 720 } else { 721 return allTaglets.get(name); 722 } 723 724 } 725 726 /* 727 * The output of this method is the basis for a table at the end of the 728 * doc comment specification, so any changes in the output may indicate 729 * a need for a corresponding update to the spec. 730 */ 731 private void showTaglets(PrintStream out) { 732 Set<Taglet> taglets = new TreeSet<>((o1, o2) -> o1.getName().compareTo(o2.getName())); 733 taglets.addAll(allTaglets.values()); 734 735 for (Taglet t : taglets) { 736 String name = t.isInlineTag() ? "{@" + t.getName() + "}" : "@" + t.getName(); 737 out.println(String.format("%20s", name) + ": " 738 + format(t.inOverview(), "overview") + " " 739 + format(t.inModule(), "module") + " " 740 + format(t.inPackage(), "package") + " " 741 + format(t.inType(), "type") + " " 742 + format(t.inConstructor(),"constructor") + " " 743 + format(t.inMethod(), "method") + " " 744 + format(t.inField(), "field") + " " 745 + format(t.isInlineTag(), "inline")+ " " 746 + format((t instanceof SimpleTaglet) && !((SimpleTaglet)t).enabled, "disabled")); 747 } 748 } 749 750 private String format(boolean b, String s) { 751 return b ? s : s.replaceAll(".", "."); // replace all with "." 752 } 753 }