TagletManager.java revision 3474:dcb234002184
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.ModuleElement;
35import javax.lang.model.element.PackageElement;
36import javax.lang.model.element.TypeElement;
37import javax.lang.model.element.VariableElement;
38import javax.lang.model.util.SimpleElementVisitor9;
39import javax.tools.JavaFileManager;
40import javax.tools.StandardJavaFileManager;
41
42import com.sun.source.doctree.DocTree;
43import com.sun.tools.javac.util.DefinedBy;
44import com.sun.tools.javac.util.DefinedBy.Api;
45
46import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
47import jdk.javadoc.internal.doclets.toolkit.util.MessageRetriever;
48import jdk.javadoc.internal.doclets.toolkit.util.Utils;
49
50import static javax.tools.DocumentationTool.Location.*;
51
52import 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
65public 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}
814