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