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