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