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