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