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