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