TagletManager.java revision 3294:9adfb22ff08f
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            ensureReadable(customTagClass);
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     * Ensures that the module of the given class is readable to this
266     * module.
267     * @param targetClass class in module to be made readable
268     */
269    private void ensureReadable(Class<?> targetClass) {
270        try {
271            Method getModuleMethod = Class.class.getMethod("getModule");
272            Object thisModule = getModuleMethod.invoke(this.getClass());
273            Object targetModule = getModuleMethod.invoke(targetClass);
274
275            Class<?> moduleClass = getModuleMethod.getReturnType();
276            Method addReadsMethod = moduleClass.getMethod("addReads", moduleClass);
277            addReadsMethod.invoke(thisModule, targetModule);
278        } catch (NoSuchMethodException e) {
279            // ignore
280        } catch (Exception e) {
281            throw new InternalError(e.toString());
282        }
283    }
284
285    /**
286     * Add a new <code>SimpleTaglet</code>.  If this tag already exists
287     * and the header passed as an argument is null, move tag to the back of the
288     * list. If this tag already exists and the header passed as an argument is
289     * not null, overwrite previous tag with new one.  Otherwise, add new
290     * SimpleTaglet to list.
291     * @param tagName the name of this tag
292     * @param header the header to output.
293     * @param locations the possible locations that this tag
294     * can appear in.
295     */
296    public void addNewSimpleCustomTag(String tagName, String header, String locations) {
297        if (tagName == null || locations == null) {
298            return;
299        }
300        Taglet tag = customTags.get(tagName);
301        locations = Utils.toLowerCase(locations);
302        if (tag == null || header != null) {
303            customTags.remove(tagName);
304            customTags.put(tagName, new SimpleTaglet(tagName, header, locations));
305            if (locations != null && locations.indexOf('x') == -1) {
306                checkTagName(tagName);
307            }
308        } else {
309            //Move to back
310            customTags.remove(tagName);
311            customTags.put(tagName, tag);
312        }
313    }
314
315    /**
316     * Given a tag name, add it to the set of tags it belongs to.
317     */
318    private void checkTagName(String name) {
319        if (standardTags.contains(name)) {
320            overridenStandardTags.add(name);
321        } else {
322            if (name.indexOf('.') == -1) {
323                potentiallyConflictingTags.add(name);
324            }
325            unseenCustomTags.add(name);
326        }
327    }
328
329    /**
330     * Check the taglet to see if it is a legacy taglet.  Also
331     * check its name for errors.
332     */
333    private void checkTaglet(Object taglet) {
334        if (taglet instanceof Taglet) {
335            checkTagName(((Taglet) taglet).getName());
336        } else if (taglet instanceof jdk.javadoc.doclet.taglet.Taglet) {
337            jdk.javadoc.doclet.taglet.Taglet legacyTaglet = (jdk.javadoc.doclet.taglet.Taglet) taglet;
338            customTags.remove(legacyTaglet.getName());
339            customTags.put(legacyTaglet.getName(), new UserTaglet(legacyTaglet));
340            checkTagName(legacyTaglet.getName());
341        } else {
342            throw new IllegalArgumentException("Given object is not a taglet.");
343        }
344    }
345
346    /**
347     * Given a name of a seen custom tag, remove it from the set of unseen
348     * custom tags.
349     * @param name the name of the seen custom tag.
350     */
351    public void seenCustomTag(String name) {
352        unseenCustomTags.remove(name);
353    }
354
355    /**
356     * Given an array of <code>Tag</code>s, check for spelling mistakes.
357     * @param utils the utility class to use
358     * @param element the tags holder
359     * @param trees the trees containing the comments
360     * @param areInlineTags true if the array of tags are inline and false otherwise.
361     */
362    public void checkTags(final Utils utils, Element element,
363                          Iterable<? extends DocTree> trees, boolean areInlineTags) {
364        if (trees == null) {
365            return;
366        }
367        CommentHelper ch = utils.getCommentHelper(element);
368        for (DocTree tag : trees) {
369            String name = tag.getKind().tagName;
370            if (name == null) {
371                continue;
372            }
373            if (name.length() > 0 && name.charAt(0) == '@') {
374                name = name.substring(1, name.length());
375            }
376            if (! (standardTags.contains(name) || customTags.containsKey(name))) {
377                if (standardTagsLowercase.contains(Utils.toLowerCase(name))) {
378                    message.warning(ch.getDocTreePath(tag), "doclet.UnknownTagLowercase", ch.getTagName(tag));
379                    continue;
380                } else {
381                    message.warning(ch.getDocTreePath(tag), "doclet.UnknownTag", ch.getTagName(tag));
382                    continue;
383                }
384            }
385            final Taglet taglet = customTags.get(name);
386            // Check and verify tag usage
387            if (taglet != null) {
388                if (areInlineTags && !taglet.isInlineTag()) {
389                    printTagMisuseWarn(ch, taglet, tag, "inline");
390                }
391                // nothing more to do
392                if (element == null) {
393                    return;
394                }
395                new SimpleElementVisitor9<Void, Void>() {
396                    @Override @DefinedBy(Api.LANGUAGE_MODEL)
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 @DefinedBy(Api.LANGUAGE_MODEL)
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 @DefinedBy(Api.LANGUAGE_MODEL)
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 @DefinedBy(Api.LANGUAGE_MODEL)
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 @DefinedBy(Api.LANGUAGE_MODEL)
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 @DefinedBy(Api.LANGUAGE_MODEL)
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.inPackage()) {
460            locationsSet.add("package");
461        }
462        if (taglet.inType()) {
463            locationsSet.add("class/interface");
464        }
465        if (taglet.inConstructor())  {
466            locationsSet.add("constructor");
467        }
468        if (taglet.inField()) {
469            locationsSet.add("field");
470        }
471        if (taglet.inMethod()) {
472            locationsSet.add("method");
473        }
474        if (taglet.isInlineTag()) {
475            locationsSet.add("inline text");
476        }
477        String[] locations = locationsSet.toArray(new String[]{});
478        if (locations == null || locations.length == 0) {
479            //This known tag is excluded.
480            return;
481        }
482        StringBuilder combined_locations = new StringBuilder();
483        for (int i = 0; i < locations.length; i++) {
484            if (i > 0) {
485                combined_locations.append(", ");
486            }
487            combined_locations.append(locations[i]);
488        }
489        message.warning(ch.getDocTreePath(tag), "doclet.tag_misuse",
490            "@" + taglet.getName(), holderType, combined_locations.toString());
491    }
492
493    /**
494     * Return the array of <code>Taglet</code>s that can
495     * appear in packages.
496     * @return the array of <code>Taglet</code>s that can
497     * appear in packages.
498     */
499    public List<Taglet> getPackageCustomTaglets() {
500        if (packageTags == null) {
501            initCustomTaglets();
502        }
503        return packageTags;
504    }
505
506    /**
507     * Return the array of <code>Taglet</code>s that can
508     * appear in classes or interfaces.
509     * @return the array of <code>Taglet</code>s that can
510     * appear in classes or interfaces.
511     */
512    public List<Taglet> getTypeCustomTaglets() {
513        if (typeTags == null) {
514            initCustomTaglets();
515        }
516        return typeTags;
517    }
518
519    /**
520     * Return the array of inline <code>Taglet</code>s that can
521     * appear in comments.
522     * @return the array of <code>Taglet</code>s that can
523     * appear in comments.
524     */
525    public List<Taglet> getInlineCustomTaglets() {
526        if (inlineTags == null) {
527            initCustomTaglets();
528        }
529        return inlineTags;
530    }
531
532    /**
533     * Return the array of <code>Taglet</code>s that can
534     * appear in fields.
535     * @return the array of <code>Taglet</code>s that can
536     * appear in field.
537     */
538    public List<Taglet> getFieldCustomTaglets() {
539        if (fieldTags == null) {
540            initCustomTaglets();
541        }
542        return fieldTags;
543    }
544
545    /**
546     * Return the array of <code>Taglet</code>s that can
547     * appear in the serialized form.
548     * @return the array of <code>Taglet</code>s that can
549     * appear in the serialized form.
550     */
551    public List<Taglet> getSerializedFormTaglets() {
552        if (serializedFormTags == null) {
553            initCustomTaglets();
554        }
555        return serializedFormTags;
556    }
557
558    /**
559     * Returns the custom tags for a given element.
560     *
561     * @param e the element to get custom tags for
562     * @return the array of <code>Taglet</code>s that can
563     * appear in the given element.
564     */
565    public List<Taglet> getCustomTaglets(Element e) {
566        switch (e.getKind()) {
567            case CONSTRUCTOR:
568                return getConstructorCustomTaglets();
569            case METHOD:
570                return getMethodCustomTaglets();
571            case ENUM_CONSTANT:
572            case FIELD:
573                return getFieldCustomTaglets();
574            case ANNOTATION_TYPE:
575            case INTERFACE:
576            case CLASS:
577            case ENUM:
578                return getTypeCustomTaglets();
579            case PACKAGE:
580                return getPackageCustomTaglets();
581            case OTHER:
582                return getOverviewCustomTaglets();
583            default:
584                throw new AssertionError("unknown element: " + e + " ,kind: " + e.getKind());
585        }
586    }
587
588    /**
589     * Return a List of <code>Taglet</code>s that can
590     * appear in constructors.
591     * @return the array of <code>Taglet</code>s that can
592     * appear in constructors.
593     */
594    public List<Taglet> getConstructorCustomTaglets() {
595        if (constructorTags == null) {
596            initCustomTaglets();
597        }
598        return constructorTags;
599    }
600
601    /**
602     * Return a List of <code>Taglet</code>s that can
603     * appear in methods.
604     * @return the array of <code>Taglet</code>s that can
605     * appear in methods.
606     */
607    public List<Taglet> getMethodCustomTaglets() {
608        if (methodTags == null) {
609            initCustomTaglets();
610        }
611        return methodTags;
612    }
613
614    /**
615     * Return a List of <code>Taglet</code>s that can
616     * appear in an overview.
617     * @return the array of <code>Taglet</code>s that can
618     * appear in overview.
619     */
620    public List<Taglet> getOverviewCustomTaglets() {
621        if (overviewTags == null) {
622            initCustomTaglets();
623        }
624        return overviewTags;
625    }
626
627    /**
628     * Initialize the custom tag Lists.
629     */
630    private void initCustomTaglets() {
631
632        packageTags = new ArrayList<>();
633        typeTags = new ArrayList<>();
634        fieldTags = new ArrayList<>();
635        constructorTags = new ArrayList<>();
636        methodTags = new ArrayList<>();
637        inlineTags = new ArrayList<>();
638        overviewTags = new ArrayList<>();
639
640        for (Taglet current : customTags.values()) {
641            if (current.inPackage() && !current.isInlineTag()) {
642                packageTags.add(current);
643            }
644            if (current.inType() && !current.isInlineTag()) {
645                typeTags.add(current);
646            }
647            if (current.inField() && !current.isInlineTag()) {
648                fieldTags.add(current);
649            }
650            if (current.inConstructor() && !current.isInlineTag()) {
651                constructorTags.add(current);
652            }
653            if (current.inMethod() && !current.isInlineTag()) {
654                methodTags.add(current);
655            }
656            if (current.isInlineTag()) {
657                inlineTags.add(current);
658            }
659            if (current.inOverview() && !current.isInlineTag()) {
660                overviewTags.add(current);
661            }
662        }
663
664        //Init the serialized form tags
665        serializedFormTags = new ArrayList<>();
666        serializedFormTags.add(customTags.get(SERIAL_DATA.tagName));
667        serializedFormTags.add(customTags.get(THROWS.tagName));
668        if (!nosince)
669            serializedFormTags.add(customTags.get(SINCE.tagName));
670        serializedFormTags.add(customTags.get(SEE.tagName));
671    }
672
673    /**
674     * Initialize standard Javadoc tags for ordering purposes.
675     */
676    private void initStandardTaglets() {
677        if (javafx) {
678            initJavaFXTaglets();
679        }
680
681        Taglet temp;
682        addStandardTaglet(new ParamTaglet());
683        addStandardTaglet(new ReturnTaglet());
684        addStandardTaglet(new ThrowsTaglet());
685        addStandardTaglet(new SimpleTaglet(EXCEPTION.tagName, null,
686                SimpleTaglet.METHOD + SimpleTaglet.CONSTRUCTOR));
687        addStandardTaglet(!nosince, new SimpleTaglet(SINCE.tagName, message.getText("doclet.Since"),
688               SimpleTaglet.ALL));
689        addStandardTaglet(showversion, new SimpleTaglet(VERSION.tagName, message.getText("doclet.Version"),
690                SimpleTaglet.PACKAGE + SimpleTaglet.TYPE + SimpleTaglet.OVERVIEW));
691        addStandardTaglet(showauthor, new SimpleTaglet(AUTHOR.tagName, message.getText("doclet.Author"),
692                SimpleTaglet.PACKAGE + SimpleTaglet.TYPE + SimpleTaglet.OVERVIEW));
693        addStandardTaglet(new SimpleTaglet(SERIAL_DATA.tagName, message.getText("doclet.SerialData"),
694            SimpleTaglet.EXCLUDED));
695        customTags.put((temp = new SimpleTaglet("factory", message.getText("doclet.Factory"),
696            SimpleTaglet.METHOD)).getName(), temp);
697        addStandardTaglet(new SeeTaglet());
698        //Standard inline tags
699        addStandardTaglet(new DocRootTaglet());
700        addStandardTaglet(new InheritDocTaglet());
701        addStandardTaglet(new ValueTaglet());
702        addStandardTaglet(new LiteralTaglet());
703        addStandardTaglet(new CodeTaglet());
704        addStandardTaglet(new IndexTaglet());
705
706        // Keep track of the names of standard tags for error
707        // checking purposes. The following are not handled above.
708        standardTags.add(DEPRECATED.tagName);
709        standardTags.add(LINK.tagName);
710        standardTags.add(LINK_PLAIN.tagName);
711        standardTags.add(SERIAL.tagName);
712        standardTags.add(SERIAL_FIELD.tagName);
713    }
714
715    /**
716     * Initialize JavaFX-related tags.
717     */
718    private void initJavaFXTaglets() {
719        addStandardTaglet(new PropertyGetterTaglet());
720        addStandardTaglet(new PropertySetterTaglet());
721        addStandardTaglet(new SimpleTaglet("propertyDescription",
722                message.getText("doclet.PropertyDescription"),
723                SimpleTaglet.FIELD + SimpleTaglet.METHOD));
724        addStandardTaglet(new SimpleTaglet("defaultValue", message.getText("doclet.DefaultValue"),
725            SimpleTaglet.FIELD + SimpleTaglet.METHOD));
726        addStandardTaglet(new SimpleTaglet("treatAsPrivate", null,
727                SimpleTaglet.FIELD + SimpleTaglet.METHOD + SimpleTaglet.TYPE));
728    }
729
730    void addStandardTaglet(Taglet taglet) {
731        String name = taglet.getName();
732        customTags.put(name, taglet);
733        standardTags.add(name);
734    }
735
736    void addStandardTaglet(boolean enable, Taglet taglet) {
737        String name = taglet.getName();
738        if (enable)
739            customTags.put(name, taglet);
740        standardTags.add(name);
741    }
742
743    /**
744     * Initialize lowercase version of standard Javadoc tags.
745     */
746    private void initStandardTagsLowercase() {
747        for (String standardTag : standardTags) {
748            standardTagsLowercase.add(Utils.toLowerCase(standardTag));
749        }
750    }
751
752    public boolean isKnownCustomTag(String tagName) {
753        return customTags.containsKey(tagName);
754    }
755
756    /**
757     * Print a list of {@link Taglet}s that might conflict with
758     * standard tags in the future and a list of standard tags
759     * that have been overriden.
760     */
761    public void printReport() {
762        printReportHelper("doclet.Notice_taglet_conflict_warn", potentiallyConflictingTags);
763        printReportHelper("doclet.Notice_taglet_overriden", overridenStandardTags);
764        printReportHelper("doclet.Notice_taglet_unseen", unseenCustomTags);
765    }
766
767    private void printReportHelper(String noticeKey, Set<String> names) {
768        if (names.size() > 0) {
769            String[] namesArray = names.toArray(new String[] {});
770            String result = " ";
771            for (int i = 0; i < namesArray.length; i++) {
772                result += "@" + namesArray[i];
773                if (i + 1 < namesArray.length) {
774                    result += ", ";
775                }
776            }
777            message.notice(noticeKey, result);
778        }
779    }
780
781    /**
782     * Given the name of a tag, return the corresponding taglet.
783     * Return null if the tag is unknown.
784     *
785     * @param name the name of the taglet to retrieve.
786     * @return return the corresponding taglet. Return null if the tag is
787     *         unknown.
788     */
789    public Taglet getTaglet(String name) {
790        if (name.indexOf("@") == 0) {
791            return customTags.get(name.substring(1));
792        } else {
793            return customTags.get(name);
794        }
795
796    }
797}
798