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 }