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