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.lang.reflect.Method;
  30 import java.util.*;
  31 
  32 import javax.lang.model.element.Element;
  33 import javax.lang.model.element.ExecutableElement;
  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 com.sun.tools.javac.util.DefinedBy;
  43 import com.sun.tools.javac.util.DefinedBy.Api;
  44 
  45 import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
  46 import jdk.javadoc.internal.doclets.toolkit.util.MessageRetriever;
  47 import jdk.javadoc.internal.doclets.toolkit.util.Utils;
  48 
  49 import static javax.tools.DocumentationTool.Location.*;
  50 
  51 import static com.sun.source.doctree.DocTree.Kind.*;
  52 
  53 /**
  54  * Manages the {@code Taglet}s used by doclets.
  55  *
  56  *  <p><b>This is NOT part of any supported API.
  57  *  If you write code that depends on this, you do so at your own risk.
  58  *  This code and its internal interfaces are subject to change or
  59  *  deletion without notice.</b>
  60  *
  61  * @author Jamie Ho
  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 = null;
 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                 if (fileManager instanceof StandardJavaFileManager) {
 244                     ((StandardJavaFileManager) fileManager).setLocation(TAGLET_PATH, paths);
 245                 }
 246             }
 247             tagClassLoader = fileManager.getClassLoader(TAGLET_PATH);
 248             Class<?> customTagClass = tagClassLoader.loadClass(classname);
 249             ensureReadable(customTagClass);
 250             @SuppressWarnings("deprecation")
 251             Object instance = customTagClass.newInstance();
 252             Taglet newLegacy = new UserTaglet((jdk.javadoc.doclet.taglet.Taglet)instance);
 253             String tname = newLegacy.getName();
 254             Taglet t = customTags.get(tname);
 255             if (t != null) {
 256                 customTags.remove(tname);
 257             }
 258             customTags.put(tname, newLegacy);
 259             message.notice("doclet.Notice_taglet_registered", classname);
 260         } catch (Exception exc) {
 261             message.error("doclet.Error_taglet_not_registered", exc.getClass().getName(), classname);
 262         }
 263     }
 264 
 265     /**
 266      * Ensures that the module of the given class is readable to this
 267      * module.
 268      * @param targetClass class in module to be made readable
 269      */
 270     private void ensureReadable(Class<?> targetClass) {
 271         try {
 272             Method getModuleMethod = Class.class.getMethod("getModule");
 273             Object thisModule = getModuleMethod.invoke(this.getClass());
 274             Object targetModule = getModuleMethod.invoke(targetClass);
 275 
 276             Class<?> moduleClass = getModuleMethod.getReturnType();
 277             Method addReadsMethod = moduleClass.getMethod("addReads", moduleClass);
 278             addReadsMethod.invoke(thisModule, targetModule);
 279         } catch (NoSuchMethodException e) {
 280             // ignore
 281         } catch (Exception e) {
 282             throw new InternalError(e.toString());
 283         }
 284     }
 285 
 286     /**
 287      * Add a new <code>SimpleTaglet</code>.  If this tag already exists
 288      * and the header passed as an argument is null, move tag to the back of the
 289      * list. If this tag already exists and the header passed as an argument is
 290      * not null, overwrite previous tag with new one.  Otherwise, add new
 291      * SimpleTaglet to list.
 292      * @param tagName the name of this tag
 293      * @param header the header to output.
 294      * @param locations the possible locations that this tag
 295      * can appear in.
 296      */
 297     public void addNewSimpleCustomTag(String tagName, String header, String locations) {
 298         if (tagName == null || locations == null) {
 299             return;
 300         }
 301         Taglet tag = customTags.get(tagName);
 302         locations = Utils.toLowerCase(locations);
 303         if (tag == null || header != null) {
 304             customTags.remove(tagName);
 305             customTags.put(tagName, new SimpleTaglet(tagName, header, locations));
 306             if (locations != null && locations.indexOf('x') == -1) {
 307                 checkTagName(tagName);
 308             }
 309         } else {
 310             //Move to back
 311             customTags.remove(tagName);
 312             customTags.put(tagName, tag);
 313         }
 314     }
 315 
 316     /**
 317      * Given a tag name, add it to the set of tags it belongs to.
 318      */
 319     private void checkTagName(String name) {
 320         if (standardTags.contains(name)) {
 321             overridenStandardTags.add(name);
 322         } else {
 323             if (name.indexOf('.') == -1) {
 324                 potentiallyConflictingTags.add(name);
 325             }
 326             unseenCustomTags.add(name);
 327         }
 328     }
 329 
 330     /**
 331      * Check the taglet to see if it is a legacy taglet.  Also
 332      * check its name for errors.
 333      */
 334     private void checkTaglet(Object taglet) {
 335         if (taglet instanceof Taglet) {
 336             checkTagName(((Taglet) taglet).getName());
 337         } else if (taglet instanceof jdk.javadoc.doclet.taglet.Taglet) {
 338             jdk.javadoc.doclet.taglet.Taglet legacyTaglet = (jdk.javadoc.doclet.taglet.Taglet) taglet;
 339             customTags.remove(legacyTaglet.getName());
 340             customTags.put(legacyTaglet.getName(), new UserTaglet(legacyTaglet));
 341             checkTagName(legacyTaglet.getName());
 342         } else {
 343             throw new IllegalArgumentException("Given object is not a taglet.");
 344         }
 345     }
 346 
 347     /**
 348      * Given a name of a seen custom tag, remove it from the set of unseen
 349      * custom tags.
 350      * @param name the name of the seen custom tag.
 351      */
 352     public void seenCustomTag(String name) {
 353         unseenCustomTags.remove(name);
 354     }
 355 
 356     /**
 357      * Given an array of <code>Tag</code>s, check for spelling mistakes.
 358      * @param utils the utility class to use
 359      * @param element the tags holder
 360      * @param trees the trees containing the comments
 361      * @param areInlineTags true if the array of tags are inline and false otherwise.
 362      */
 363     public void checkTags(final Utils utils, Element element,
 364                           Iterable<? extends DocTree> trees, boolean areInlineTags) {
 365         if (trees == null) {
 366             return;
 367         }
 368         CommentHelper ch = utils.getCommentHelper(element);
 369         for (DocTree tag : trees) {
 370             String name = tag.getKind().tagName;
 371             if (name == null) {
 372                 continue;
 373             }
 374             if (name.length() > 0 && name.charAt(0) == '@') {
 375                 name = name.substring(1, name.length());
 376             }
 377             if (! (standardTags.contains(name) || customTags.containsKey(name))) {
 378                 if (standardTagsLowercase.contains(Utils.toLowerCase(name))) {
 379                     message.warning(ch.getDocTreePath(tag), "doclet.UnknownTagLowercase", ch.getTagName(tag));
 380                     continue;
 381                 } else {
 382                     message.warning(ch.getDocTreePath(tag), "doclet.UnknownTag", ch.getTagName(tag));
 383                     continue;
 384                 }
 385             }
 386             final Taglet taglet = customTags.get(name);
 387             // Check and verify tag usage
 388             if (taglet != null) {
 389                 if (areInlineTags && !taglet.isInlineTag()) {
 390                     printTagMisuseWarn(ch, taglet, tag, "inline");
 391                 }
 392                 // nothing more to do
 393                 if (element == null) {
 394                     return;
 395                 }
 396                 new SimpleElementVisitor9<Void, Void>() {
 397                     @Override @DefinedBy(Api.LANGUAGE_MODEL)
 398                     public Void visitPackage(PackageElement e, Void p) {
 399                         if (!taglet.inPackage()) {
 400                             printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "package");
 401                         }
 402                         return null;
 403                     }
 404 
 405                     @Override @DefinedBy(Api.LANGUAGE_MODEL)
 406                     public Void visitType(TypeElement e, Void p) {
 407                         if (!taglet.inType()) {
 408                             printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "class");
 409                         }
 410                         return null;
 411                     }
 412 
 413                     @Override @DefinedBy(Api.LANGUAGE_MODEL)
 414                     public Void visitExecutable(ExecutableElement e, Void p) {
 415                         if (utils.isConstructor(e) && !taglet.inConstructor()) {
 416                             printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "constructor");
 417                         } else if (!taglet.inMethod()) {
 418                             printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "method");
 419                         }
 420                         return null;
 421                     }
 422 
 423                     @Override @DefinedBy(Api.LANGUAGE_MODEL)
 424                     public Void visitVariable(VariableElement e, Void p) {
 425                         if (utils.isField(e) && !taglet.inField()) {
 426                             printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "field");
 427                         }
 428                         return null;
 429                     }
 430 
 431                     @Override @DefinedBy(Api.LANGUAGE_MODEL)
 432                     public Void visitUnknown(Element e, Void p) {
 433                         if (utils.isOverviewElement(e) && !taglet.inOverview()) {
 434                             printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "overview");
 435                         }
 436                         return null;
 437                     }
 438 
 439                     @Override @DefinedBy(Api.LANGUAGE_MODEL)
 440                     protected Void defaultAction(Element e, Void p) {
 441                         return null;
 442                     }
 443                 }.visit(element);
 444             }
 445         }
 446     }
 447 
 448     /**
 449      * Given the taglet, the tag and the type of documentation that the tag
 450      * was found in, print a tag misuse warning.
 451      * @param taglet the taglet representing the misused tag.
 452      * @param tag the misused tag.
 453      * @param holderType the type of documentation that the misused tag was found in.
 454      */
 455     private void printTagMisuseWarn(CommentHelper ch, Taglet taglet, DocTree tag, String holderType) {
 456         Set<String> locationsSet = new LinkedHashSet<>();
 457         if (taglet.inOverview()) {
 458             locationsSet.add("overview");
 459         }
 460         if (taglet.inPackage()) {
 461             locationsSet.add("package");
 462         }
 463         if (taglet.inType()) {
 464             locationsSet.add("class/interface");
 465         }
 466         if (taglet.inConstructor())  {
 467             locationsSet.add("constructor");
 468         }
 469         if (taglet.inField()) {
 470             locationsSet.add("field");
 471         }
 472         if (taglet.inMethod()) {
 473             locationsSet.add("method");
 474         }
 475         if (taglet.isInlineTag()) {
 476             locationsSet.add("inline text");
 477         }
 478         String[] locations = locationsSet.toArray(new String[]{});
 479         if (locations == null || locations.length == 0) {
 480             //This known tag is excluded.
 481             return;
 482         }
 483         StringBuilder combined_locations = new StringBuilder();
 484         for (int i = 0; i < locations.length; i++) {
 485             if (i > 0) {
 486                 combined_locations.append(", ");
 487             }
 488             combined_locations.append(locations[i]);
 489         }
 490         message.warning(ch.getDocTreePath(tag), "doclet.tag_misuse",
 491             "@" + taglet.getName(), holderType, combined_locations.toString());
 492     }
 493 
 494     /**
 495      * Return the array of <code>Taglet</code>s that can
 496      * appear in packages.
 497      * @return the array of <code>Taglet</code>s that can
 498      * appear in packages.
 499      */
 500     public List<Taglet> getPackageCustomTaglets() {
 501         if (packageTags == null) {
 502             initCustomTaglets();
 503         }
 504         return packageTags;
 505     }
 506 
 507     /**
 508      * Return the array of <code>Taglet</code>s that can
 509      * appear in classes or interfaces.
 510      * @return the array of <code>Taglet</code>s that can
 511      * appear in classes or interfaces.
 512      */
 513     public List<Taglet> getTypeCustomTaglets() {
 514         if (typeTags == null) {
 515             initCustomTaglets();
 516         }
 517         return typeTags;
 518     }
 519 
 520     /**
 521      * Return the array of inline <code>Taglet</code>s that can
 522      * appear in comments.
 523      * @return the array of <code>Taglet</code>s that can
 524      * appear in comments.
 525      */
 526     public List<Taglet> getInlineCustomTaglets() {
 527         if (inlineTags == null) {
 528             initCustomTaglets();
 529         }
 530         return inlineTags;
 531     }
 532 
 533     /**
 534      * Return the array of <code>Taglet</code>s that can
 535      * appear in fields.
 536      * @return the array of <code>Taglet</code>s that can
 537      * appear in field.
 538      */
 539     public List<Taglet> getFieldCustomTaglets() {
 540         if (fieldTags == null) {
 541             initCustomTaglets();
 542         }
 543         return fieldTags;
 544     }
 545 
 546     /**
 547      * Return the array of <code>Taglet</code>s that can
 548      * appear in the serialized form.
 549      * @return the array of <code>Taglet</code>s that can
 550      * appear in the serialized form.
 551      */
 552     public List<Taglet> getSerializedFormTaglets() {
 553         if (serializedFormTags == null) {
 554             initCustomTaglets();
 555         }
 556         return serializedFormTags;
 557     }
 558 
 559     /**
 560      * Returns the custom tags for a given element.
 561      *
 562      * @param e the element to get custom tags for
 563      * @return the array of <code>Taglet</code>s that can
 564      * appear in the given element.
 565      */
 566     public List<Taglet> getCustomTaglets(Element e) {
 567         switch (e.getKind()) {
 568             case CONSTRUCTOR:
 569                 return getConstructorCustomTaglets();
 570             case METHOD:
 571                 return getMethodCustomTaglets();
 572             case ENUM_CONSTANT:
 573             case FIELD:
 574                 return getFieldCustomTaglets();
 575             case ANNOTATION_TYPE:
 576             case INTERFACE:
 577             case CLASS:
 578             case ENUM:
 579                 return getTypeCustomTaglets();
 580             case PACKAGE:
 581                 return getPackageCustomTaglets();
 582             case OTHER:
 583                 return getOverviewCustomTaglets();
 584             default:
 585                 throw new AssertionError("unknown element: " + e + " ,kind: " + e.getKind());
 586         }
 587     }
 588 
 589     /**
 590      * Return a List of <code>Taglet</code>s that can
 591      * appear in constructors.
 592      * @return the array of <code>Taglet</code>s that can
 593      * appear in constructors.
 594      */
 595     public List<Taglet> getConstructorCustomTaglets() {
 596         if (constructorTags == null) {
 597             initCustomTaglets();
 598         }
 599         return constructorTags;
 600     }
 601 
 602     /**
 603      * Return a List of <code>Taglet</code>s that can
 604      * appear in methods.
 605      * @return the array of <code>Taglet</code>s that can
 606      * appear in methods.
 607      */
 608     public List<Taglet> getMethodCustomTaglets() {
 609         if (methodTags == null) {
 610             initCustomTaglets();
 611         }
 612         return methodTags;
 613     }
 614 
 615     /**
 616      * Return a List of <code>Taglet</code>s that can
 617      * appear in an overview.
 618      * @return the array of <code>Taglet</code>s that can
 619      * appear in overview.
 620      */
 621     public List<Taglet> getOverviewCustomTaglets() {
 622         if (overviewTags == null) {
 623             initCustomTaglets();
 624         }
 625         return overviewTags;
 626     }
 627 
 628     /**
 629      * Initialize the custom tag Lists.
 630      */
 631     private void initCustomTaglets() {
 632 
 633         packageTags = new ArrayList<>();
 634         typeTags = new ArrayList<>();
 635         fieldTags = new ArrayList<>();
 636         constructorTags = new ArrayList<>();
 637         methodTags = new ArrayList<>();
 638         inlineTags = new ArrayList<>();
 639         overviewTags = new ArrayList<>();
 640 
 641         for (Taglet current : customTags.values()) {
 642             if (current.inPackage() && !current.isInlineTag()) {
 643                 packageTags.add(current);
 644             }
 645             if (current.inType() && !current.isInlineTag()) {
 646                 typeTags.add(current);
 647             }
 648             if (current.inField() && !current.isInlineTag()) {
 649                 fieldTags.add(current);
 650             }
 651             if (current.inConstructor() && !current.isInlineTag()) {
 652                 constructorTags.add(current);
 653             }
 654             if (current.inMethod() && !current.isInlineTag()) {
 655                 methodTags.add(current);
 656             }
 657             if (current.isInlineTag()) {
 658                 inlineTags.add(current);
 659             }
 660             if (current.inOverview() && !current.isInlineTag()) {
 661                 overviewTags.add(current);
 662             }
 663         }
 664 
 665         //Init the serialized form tags
 666         serializedFormTags = new ArrayList<>();
 667         serializedFormTags.add(customTags.get(SERIAL_DATA.tagName));
 668         serializedFormTags.add(customTags.get(THROWS.tagName));
 669         if (!nosince)
 670             serializedFormTags.add(customTags.get(SINCE.tagName));
 671         serializedFormTags.add(customTags.get(SEE.tagName));
 672     }
 673 
 674     /**
 675      * Initialize standard Javadoc tags for ordering purposes.
 676      */
 677     private void initStandardTaglets() {
 678         if (javafx) {
 679             initJavaFXTaglets();
 680         }
 681 
 682         Taglet temp;
 683         addStandardTaglet(new ParamTaglet());
 684         addStandardTaglet(new ReturnTaglet());
 685         addStandardTaglet(new ThrowsTaglet());
 686         addStandardTaglet(new SimpleTaglet(EXCEPTION.tagName, null,
 687                 SimpleTaglet.METHOD + SimpleTaglet.CONSTRUCTOR));
 688         addStandardTaglet(!nosince, new SimpleTaglet(SINCE.tagName, message.getText("doclet.Since"),
 689                 SimpleTaglet.ALL));
 690         addStandardTaglet(showversion, new SimpleTaglet(VERSION.tagName, message.getText("doclet.Version"),
 691                 SimpleTaglet.PACKAGE + SimpleTaglet.TYPE + SimpleTaglet.OVERVIEW));
 692         addStandardTaglet(showauthor, new SimpleTaglet(AUTHOR.tagName, message.getText("doclet.Author"),
 693                 SimpleTaglet.PACKAGE + SimpleTaglet.TYPE + SimpleTaglet.OVERVIEW));
 694         addStandardTaglet(new SimpleTaglet(SERIAL_DATA.tagName, message.getText("doclet.SerialData"),
 695                 SimpleTaglet.EXCLUDED));
 696         addStandardTaglet(new SimpleTaglet(HIDDEN.tagName, message.getText("doclet.Hidden"),
 697                 SimpleTaglet.FIELD + SimpleTaglet.METHOD + SimpleTaglet.TYPE));
 698         customTags.put((temp = new SimpleTaglet("factory", message.getText("doclet.Factory"),
 699                 SimpleTaglet.METHOD)).getName(), temp);
 700         addStandardTaglet(new SeeTaglet());
 701         //Standard inline tags
 702         addStandardTaglet(new DocRootTaglet());
 703         addStandardTaglet(new InheritDocTaglet());
 704         addStandardTaglet(new ValueTaglet());
 705         addStandardTaglet(new LiteralTaglet());
 706         addStandardTaglet(new CodeTaglet());
 707         addStandardTaglet(new IndexTaglet());
 708 
 709         // Keep track of the names of standard tags for error
 710         // checking purposes. The following are not handled above.
 711         standardTags.add(DEPRECATED.tagName);
 712         standardTags.add(LINK.tagName);
 713         standardTags.add(LINK_PLAIN.tagName);
 714         standardTags.add(SERIAL.tagName);
 715         standardTags.add(SERIAL_FIELD.tagName);
 716     }
 717 
 718     /**
 719      * Initialize JavaFX-related tags.
 720      */
 721     private void initJavaFXTaglets() {
 722         addStandardTaglet(new PropertyGetterTaglet());
 723         addStandardTaglet(new PropertySetterTaglet());
 724         addStandardTaglet(new SimpleTaglet("propertyDescription",
 725                 message.getText("doclet.PropertyDescription"),
 726                 SimpleTaglet.FIELD + SimpleTaglet.METHOD));
 727         addStandardTaglet(new SimpleTaglet("defaultValue", message.getText("doclet.DefaultValue"),
 728             SimpleTaglet.FIELD + SimpleTaglet.METHOD));
 729         addStandardTaglet(new SimpleTaglet("treatAsPrivate", null,
 730                 SimpleTaglet.FIELD + SimpleTaglet.METHOD + SimpleTaglet.TYPE));
 731     }
 732 
 733     void addStandardTaglet(Taglet taglet) {
 734         String name = taglet.getName();
 735         customTags.put(name, taglet);
 736         standardTags.add(name);
 737     }
 738 
 739     void addStandardTaglet(boolean enable, Taglet taglet) {
 740         String name = taglet.getName();
 741         if (enable)
 742             customTags.put(name, taglet);
 743         standardTags.add(name);
 744     }
 745 
 746     /**
 747      * Initialize lowercase version of standard Javadoc tags.
 748      */
 749     private void initStandardTagsLowercase() {
 750         for (String standardTag : standardTags) {
 751             standardTagsLowercase.add(Utils.toLowerCase(standardTag));
 752         }
 753     }
 754 
 755     public boolean isKnownCustomTag(String tagName) {
 756         return customTags.containsKey(tagName);
 757     }
 758 
 759     /**
 760      * Print a list of {@link Taglet}s that might conflict with
 761      * standard tags in the future and a list of standard tags
 762      * that have been overriden.
 763      */
 764     public void printReport() {
 765         printReportHelper("doclet.Notice_taglet_conflict_warn", potentiallyConflictingTags);
 766         printReportHelper("doclet.Notice_taglet_overriden", overridenStandardTags);
 767         printReportHelper("doclet.Notice_taglet_unseen", unseenCustomTags);
 768     }
 769 
 770     private void printReportHelper(String noticeKey, Set<String> names) {
 771         if (names.size() > 0) {
 772             String[] namesArray = names.toArray(new String[] {});
 773             String result = " ";
 774             for (int i = 0; i < namesArray.length; i++) {
 775                 result += "@" + namesArray[i];
 776                 if (i + 1 < namesArray.length) {
 777                     result += ", ";
 778                 }
 779             }
 780             message.notice(noticeKey, result);
 781         }
 782     }
 783 
 784     /**
 785      * Given the name of a tag, return the corresponding taglet.
 786      * Return null if the tag is unknown.
 787      *
 788      * @param name the name of the taglet to retrieve.
 789      * @return return the corresponding taglet. Return null if the tag is
 790      *         unknown.
 791      */
 792     public Taglet getTaglet(String name) {
 793         if (name.indexOf("@") == 0) {
 794             return customTags.get(name.substring(1));
 795         } else {
 796             return customTags.get(name);
 797         }
 798 
 799     }
 800 }