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