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