1 /* 2 * Copyright (c) 1997, 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; 27 28 import java.io.*; 29 import java.util.*; 30 import java.util.regex.Matcher; 31 import java.util.regex.Pattern; 32 import javax.tools.JavaFileManager; 33 34 import com.sun.javadoc.*; 35 import com.sun.tools.javac.sym.Profiles; 36 import com.sun.tools.javac.jvm.Profile; 37 import com.sun.tools.doclets.internal.toolkit.builders.BuilderFactory; 38 import com.sun.tools.doclets.internal.toolkit.taglets.*; 39 import com.sun.tools.doclets.internal.toolkit.util.*; 40 import com.sun.tools.javac.util.StringUtils; 41 42 /** 43 * Configure the output based on the options. Doclets should sub-class 44 * Configuration, to configure and add their own options. This class contains 45 * all user options which are supported by the 1.1 doclet and the standard 46 * doclet. 47 * 48 * <p><b>This is NOT part of any supported API. 49 * If you write code that depends on this, you do so at your own risk. 50 * This code and its internal interfaces are subject to change or 51 * deletion without notice.</b> 52 * 53 * @author Robert Field. 54 * @author Atul Dambalkar. 55 * @author Jamie Ho 56 */ 57 public abstract class Configuration { 58 59 /** 60 * Exception used to report a problem during setOptions. 61 */ 62 public static class Fault extends Exception { 63 private static final long serialVersionUID = 0; 64 Fault(String msg)65 Fault(String msg) { 66 super(msg); 67 } 68 Fault(String msg, Exception cause)69 Fault(String msg, Exception cause) { 70 super(msg, cause); 71 } 72 } 73 74 /** 75 * The factory for builders. 76 */ 77 protected BuilderFactory builderFactory; 78 79 /** 80 * The taglet manager. 81 */ 82 public TagletManager tagletManager; 83 84 /** 85 * The path to the builder XML input file. 86 */ 87 public String builderXMLPath; 88 89 /** 90 * The default path to the builder XML. 91 */ 92 private static final String DEFAULT_BUILDER_XML = "resources/doclet.xml"; 93 94 /** 95 * The path to Taglets 96 */ 97 public String tagletpath = ""; 98 99 /** 100 * This is true if option "-serialwarn" is used. Defualt value is false to 101 * suppress excessive warnings about serial tag. 102 */ 103 public boolean serialwarn = false; 104 105 /** 106 * The specified amount of space between tab stops. 107 */ 108 public int sourcetab; 109 110 public String tabSpaces; 111 112 /** 113 * True if we should generate browsable sources. 114 */ 115 public boolean linksource = false; 116 117 /** 118 * True if command line option "-nosince" is used. Default value is 119 * false. 120 */ 121 public boolean nosince = false; 122 123 /** 124 * True if we should recursively copy the doc-file subdirectories 125 */ 126 public boolean copydocfilesubdirs = false; 127 128 /** 129 * The META charset tag used for cross-platform viewing. 130 */ 131 public String charset = ""; 132 133 /** 134 * True if user wants to add member names as meta keywords. 135 * Set to false because meta keywords are ignored in general 136 * by most Internet search engines. 137 */ 138 public boolean keywords = false; 139 140 /** 141 * The meta tag keywords instance. 142 */ 143 public final MetaKeywords metakeywords = new MetaKeywords(this); 144 145 /** 146 * The list of doc-file subdirectories to exclude 147 */ 148 protected Set<String> excludedDocFileDirs; 149 150 /** 151 * The list of qualifiers to exclude 152 */ 153 protected Set<String> excludedQualifiers; 154 155 /** 156 * The Root of the generated Program Structure from the Doclet API. 157 */ 158 public RootDoc root; 159 160 /** 161 * Destination directory name, in which doclet will generate the entire 162 * documentation. Default is current directory. 163 */ 164 public String destDirName = ""; 165 166 /** 167 * Destination directory name, in which doclet will copy the doc-files to. 168 */ 169 public String docFileDestDirName = ""; 170 171 /** 172 * Encoding for this document. Default is default encoding for this 173 * platform. 174 */ 175 public String docencoding = null; 176 177 /** 178 * True if user wants to suppress descriptions and tags. 179 */ 180 public boolean nocomment = false; 181 182 /** 183 * Encoding for this document. Default is default encoding for this 184 * platform. 185 */ 186 public String encoding = null; 187 188 /** 189 * Generate author specific information for all the classes if @author 190 * tag is used in the doc comment and if -author option is used. 191 * <code>showauthor</code> is set to true if -author option is used. 192 * Default is don't show author information. 193 */ 194 public boolean showauthor = false; 195 196 /** 197 * Generate documentation for JavaFX getters and setters automatically 198 * by copying it from the appropriate property definition. 199 */ 200 public boolean javafx = false; 201 202 /** 203 * Generate version specific information for the all the classes 204 * if @version tag is used in the doc comment and if -version option is 205 * used. <code>showversion</code> is set to true if -version option is 206 * used.Default is don't show version information. 207 */ 208 public boolean showversion = false; 209 210 /** 211 * Sourcepath from where to read the source files. Default is classpath. 212 * 213 */ 214 public String sourcepath = ""; 215 216 /** 217 * Argument for command line option "-Xprofilespath". 218 */ 219 public String profilespath = ""; 220 221 /** 222 * Generate profiles documentation if profilespath is set and valid profiles 223 * are present. 224 */ 225 public boolean showProfiles = false; 226 227 /** 228 * Don't generate deprecated API information at all, if -nodeprecated 229 * option is used. <code>nodepracted</code> is set to true if 230 * -nodeprecated option is used. Default is generate deprected API 231 * information. 232 */ 233 public boolean nodeprecated = false; 234 235 /** 236 * The catalog of classes specified on the command-line 237 */ 238 public ClassDocCatalog classDocCatalog; 239 240 /** 241 * Message Retriever for the doclet, to retrieve message from the resource 242 * file for this Configuration, which is common for 1.1 and standard 243 * doclets. 244 * 245 * TODO: Make this private!!! 246 */ 247 public MessageRetriever message = null; 248 249 /** 250 * True if user wants to suppress time stamp in output. 251 * Default is false. 252 */ 253 public boolean notimestamp= false; 254 255 /** 256 * The package grouping instance. 257 */ 258 public final Group group = new Group(this); 259 260 /** 261 * The tracker of external package links. 262 */ 263 public final Extern extern = new Extern(this); 264 265 /** 266 * Return the build date for the doclet. 267 */ getDocletSpecificBuildDate()268 public abstract String getDocletSpecificBuildDate(); 269 270 /** 271 * This method should be defined in all those doclets(configurations), 272 * which want to derive themselves from this Configuration. This method 273 * can be used to set its own command line options. 274 * 275 * @param options The array of option names and values. 276 * @throws DocletAbortException 277 */ setSpecificDocletOptions(String[][] options)278 public abstract void setSpecificDocletOptions(String[][] options) throws Fault; 279 280 /** 281 * Return the doclet specific {@link MessageRetriever} 282 * @return the doclet specific MessageRetriever. 283 */ getDocletSpecificMsg()284 public abstract MessageRetriever getDocletSpecificMsg(); 285 286 /** 287 * A profiles object used to access profiles across various pages. 288 */ 289 public Profiles profiles; 290 291 /** 292 * An map of the profiles to packages. 293 */ 294 public Map<String,PackageDoc[]> profilePackages; 295 296 /** 297 * An array of the packages specified on the command-line merged 298 * with the array of packages that contain the classes specified on the 299 * command-line. The array is sorted. 300 */ 301 public PackageDoc[] packages; 302 303 /** 304 * Constructor. Constructs the message retriever with resource file. 305 */ Configuration()306 public Configuration() { 307 message = 308 new MessageRetriever(this, 309 "com.sun.tools.doclets.internal.toolkit.resources.doclets"); 310 excludedDocFileDirs = new HashSet<String>(); 311 excludedQualifiers = new HashSet<String>(); 312 setTabWidth(DocletConstants.DEFAULT_TAB_STOP_LENGTH); 313 } 314 315 /** 316 * Return the builder factory for this doclet. 317 * 318 * @return the builder factory for this doclet. 319 */ getBuilderFactory()320 public BuilderFactory getBuilderFactory() { 321 if (builderFactory == null) { 322 builderFactory = new BuilderFactory(this); 323 } 324 return builderFactory; 325 } 326 327 /** 328 * This method should be defined in all those doclets 329 * which want to inherit from this Configuration. This method 330 * should return the number of arguments to the command line 331 * option (including the option name). For example, 332 * -notimestamp is a single-argument option, so this method would 333 * return 1. 334 * 335 * @param option Command line option under consideration. 336 * @return number of arguments to option (including the 337 * option name). Zero return means option not known. 338 * Negative value means error occurred. 339 */ optionLength(String option)340 public int optionLength(String option) { 341 option = StringUtils.toLowerCase(option); 342 if (option.equals("-author") || 343 option.equals("-docfilessubdirs") || 344 option.equals("-javafx") || 345 option.equals("-keywords") || 346 option.equals("-linksource") || 347 option.equals("-nocomment") || 348 option.equals("-nodeprecated") || 349 option.equals("-nosince") || 350 option.equals("-notimestamp") || 351 option.equals("-quiet") || 352 option.equals("-xnodate") || 353 option.equals("-version")) { 354 return 1; 355 } else if (option.equals("-d") || 356 option.equals("-docencoding") || 357 option.equals("-encoding") || 358 option.equals("-excludedocfilessubdir") || 359 option.equals("-link") || 360 option.equals("-sourcetab") || 361 option.equals("-noqualifier") || 362 option.equals("-output") || 363 option.equals("-sourcepath") || 364 option.equals("-tag") || 365 option.equals("-taglet") || 366 option.equals("-tagletpath") || 367 option.equals("-xprofilespath")) { 368 return 2; 369 } else if (option.equals("-group") || 370 option.equals("-linkoffline")) { 371 return 3; 372 } else { 373 return -1; // indicate we don't know about it 374 } 375 } 376 377 /** 378 * Perform error checking on the given options. 379 * 380 * @param options the given options to check. 381 * @param reporter the reporter used to report errors. 382 */ validOptions(String options[][], DocErrorReporter reporter)383 public abstract boolean validOptions(String options[][], 384 DocErrorReporter reporter); 385 initProfiles()386 private void initProfiles() throws IOException { 387 if (profilespath.isEmpty()) 388 return; 389 390 profiles = Profiles.read(new File(profilespath)); 391 392 // Group the packages to be documented by the lowest profile (if any) 393 // in which each appears 394 Map<Profile, List<PackageDoc>> interimResults = 395 new EnumMap<Profile, List<PackageDoc>>(Profile.class); 396 for (Profile p: Profile.values()) 397 interimResults.put(p, new ArrayList<PackageDoc>()); 398 399 for (PackageDoc pkg: packages) { 400 if (nodeprecated && Util.isDeprecated(pkg)) { 401 continue; 402 } 403 // the getProfile method takes a type name, not a package name, 404 // but isn't particularly fussy about the simple name -- so just use * 405 int i = profiles.getProfile(pkg.name().replace(".", "/") + "/*"); 406 Profile p = Profile.lookup(i); 407 if (p != null) { 408 List<PackageDoc> pkgs = interimResults.get(p); 409 pkgs.add(pkg); 410 } 411 } 412 413 // Build the profilePackages structure used by the doclet 414 profilePackages = new HashMap<String,PackageDoc[]>(); 415 List<PackageDoc> prev = Collections.<PackageDoc>emptyList(); 416 int size; 417 for (Map.Entry<Profile,List<PackageDoc>> e: interimResults.entrySet()) { 418 Profile p = e.getKey(); 419 List<PackageDoc> pkgs = e.getValue(); 420 pkgs.addAll(prev); // each profile contains all lower profiles 421 Collections.sort(pkgs); 422 size = pkgs.size(); 423 // For a profile, if there are no packages to be documented, do not add 424 // it to profilePackages map. 425 if (size > 0) 426 profilePackages.put(p.name, pkgs.toArray(new PackageDoc[pkgs.size()])); 427 prev = pkgs; 428 } 429 430 // Generate profiles documentation if any profile contains any 431 // of the packages to be documented. 432 showProfiles = !prev.isEmpty(); 433 } 434 initPackageArray()435 private void initPackageArray() { 436 Set<PackageDoc> set = new HashSet<PackageDoc>(Arrays.asList(root.specifiedPackages())); 437 ClassDoc[] classes = root.specifiedClasses(); 438 for (int i = 0; i < classes.length; i++) { 439 set.add(classes[i].containingPackage()); 440 } 441 ArrayList<PackageDoc> results = new ArrayList<PackageDoc>(set); 442 Collections.sort(results); 443 packages = results.toArray(new PackageDoc[] {}); 444 } 445 446 /** 447 * Set the command line options supported by this configuration. 448 * 449 * @param options the two dimensional array of options. 450 */ setOptions(String[][] options)451 public void setOptions(String[][] options) throws Fault { 452 LinkedHashSet<String[]> customTagStrs = new LinkedHashSet<String[]>(); 453 454 // Some options, specifically -link and -linkoffline, require that 455 // the output directory has already been created: so do that first. 456 for (int oi = 0; oi < options.length; ++oi) { 457 String[] os = options[oi]; 458 String opt = StringUtils.toLowerCase(os[0]); 459 if (opt.equals("-d")) { 460 destDirName = addTrailingFileSep(os[1]); 461 docFileDestDirName = destDirName; 462 ensureOutputDirExists(); 463 break; 464 } 465 } 466 467 for (int oi = 0; oi < options.length; ++oi) { 468 String[] os = options[oi]; 469 String opt = StringUtils.toLowerCase(os[0]); 470 if (opt.equals("-docfilessubdirs")) { 471 copydocfilesubdirs = true; 472 } else if (opt.equals("-docencoding")) { 473 docencoding = os[1]; 474 } else if (opt.equals("-encoding")) { 475 encoding = os[1]; 476 } else if (opt.equals("-author")) { 477 showauthor = true; 478 } else if (opt.equals("-javafx")) { 479 javafx = true; 480 } else if (opt.equals("-nosince")) { 481 nosince = true; 482 } else if (opt.equals("-version")) { 483 showversion = true; 484 } else if (opt.equals("-nodeprecated")) { 485 nodeprecated = true; 486 } else if (opt.equals("-sourcepath")) { 487 sourcepath = os[1]; 488 } else if ((opt.equals("-classpath") || opt.equals("-cp")) && 489 sourcepath.length() == 0) { 490 sourcepath = os[1]; 491 } else if (opt.equals("-excludedocfilessubdir")) { 492 addToSet(excludedDocFileDirs, os[1]); 493 } else if (opt.equals("-noqualifier")) { 494 addToSet(excludedQualifiers, os[1]); 495 } else if (opt.equals("-linksource")) { 496 linksource = true; 497 } else if (opt.equals("-sourcetab")) { 498 linksource = true; 499 try { 500 setTabWidth(Integer.parseInt(os[1])); 501 } catch (NumberFormatException e) { 502 //Set to -1 so that warning will be printed 503 //to indicate what is valid argument. 504 sourcetab = -1; 505 } 506 if (sourcetab <= 0) { 507 message.warning("doclet.sourcetab_warning"); 508 setTabWidth(DocletConstants.DEFAULT_TAB_STOP_LENGTH); 509 } 510 } else if (opt.equals("-notimestamp")) { 511 notimestamp = true; 512 } else if (opt.equals("-nocomment")) { 513 nocomment = true; 514 } else if (opt.equals("-tag") || opt.equals("-taglet")) { 515 customTagStrs.add(os); 516 } else if (opt.equals("-tagletpath")) { 517 tagletpath = os[1]; 518 } else if (opt.equals("-xprofilespath")) { 519 profilespath = os[1]; 520 } else if (opt.equals("-keywords")) { 521 keywords = true; 522 } else if (opt.equals("-serialwarn")) { 523 serialwarn = true; 524 } else if (opt.equals("-group")) { 525 group.checkPackageGroups(os[1], os[2]); 526 } else if (opt.equals("-link")) { 527 String url = os[1]; 528 extern.link(url, url, root, false); 529 } else if (opt.equals("-linkoffline")) { 530 String url = os[1]; 531 String pkglisturl = os[2]; 532 extern.link(url, pkglisturl, root, true); 533 } 534 } 535 if (sourcepath.length() == 0) { 536 sourcepath = System.getProperty("env.class.path") == null ? "" : 537 System.getProperty("env.class.path"); 538 } 539 if (docencoding == null) { 540 docencoding = encoding; 541 } 542 543 classDocCatalog = new ClassDocCatalog(root.specifiedClasses(), this); 544 initTagletManager(customTagStrs); 545 } 546 547 /** 548 * Set the command line options supported by this configuration. 549 * 550 * @throws DocletAbortException 551 */ setOptions()552 public void setOptions() throws Fault { 553 initPackageArray(); 554 setOptions(root.options()); 555 try { 556 initProfiles(); 557 } catch (Exception e) { 558 throw new DocletAbortException(e); 559 } 560 setSpecificDocletOptions(root.options()); 561 } 562 ensureOutputDirExists()563 private void ensureOutputDirExists() throws Fault { 564 DocFile destDir = DocFile.createFileForDirectory(this, destDirName); 565 if (!destDir.exists()) { 566 //Create the output directory (in case it doesn't exist yet) 567 root.printNotice(getText("doclet.dest_dir_create", destDirName)); 568 destDir.mkdirs(); 569 } else if (!destDir.isDirectory()) { 570 throw new Fault(getText( 571 "doclet.destination_directory_not_directory_0", 572 destDir.getPath())); 573 } else if (!destDir.canWrite()) { 574 throw new Fault(getText( 575 "doclet.destination_directory_not_writable_0", 576 destDir.getPath())); 577 } 578 } 579 580 581 /** 582 * Initialize the taglet manager. The strings to initialize the simple custom tags should 583 * be in the following format: "[tag name]:[location str]:[heading]". 584 * @param customTagStrs the set two dimensional arrays of strings. These arrays contain 585 * either -tag or -taglet arguments. 586 */ initTagletManager(Set<String[]> customTagStrs)587 private void initTagletManager(Set<String[]> customTagStrs) { 588 tagletManager = tagletManager == null ? 589 new TagletManager(nosince, showversion, showauthor, javafx, message) : 590 tagletManager; 591 String[] args; 592 for (Iterator<String[]> it = customTagStrs.iterator(); it.hasNext(); ) { 593 args = it.next(); 594 if (args[0].equals("-taglet")) { 595 tagletManager.addCustomTag(args[1], getFileManager(), tagletpath); 596 continue; 597 } 598 String[] tokens = tokenize(args[1], 599 TagletManager.SIMPLE_TAGLET_OPT_SEPARATOR, 3); 600 if (tokens.length == 1) { 601 String tagName = args[1]; 602 if (tagletManager.isKnownCustomTag(tagName)) { 603 //reorder a standard tag 604 tagletManager.addNewSimpleCustomTag(tagName, null, ""); 605 } else { 606 //Create a simple tag with the heading that has the same name as the tag. 607 StringBuilder heading = new StringBuilder(tagName + ":"); 608 heading.setCharAt(0, Character.toUpperCase(tagName.charAt(0))); 609 tagletManager.addNewSimpleCustomTag(tagName, heading.toString(), "a"); 610 } 611 } else if (tokens.length == 2) { 612 //Add simple taglet without heading, probably to excluding it in the output. 613 tagletManager.addNewSimpleCustomTag(tokens[0], tokens[1], ""); 614 } else if (tokens.length >= 3) { 615 tagletManager.addNewSimpleCustomTag(tokens[0], tokens[2], tokens[1]); 616 } else { 617 message.error("doclet.Error_invalid_custom_tag_argument", args[1]); 618 } 619 } 620 } 621 622 /** 623 * Given a string, return an array of tokens. The separator can be escaped 624 * with the '\' character. The '\' character may also be escaped by the 625 * '\' character. 626 * 627 * @param s the string to tokenize. 628 * @param separator the separator char. 629 * @param maxTokens the maximum number of tokens returned. If the 630 * max is reached, the remaining part of s is appended 631 * to the end of the last token. 632 * 633 * @return an array of tokens. 634 */ tokenize(String s, char separator, int maxTokens)635 private String[] tokenize(String s, char separator, int maxTokens) { 636 List<String> tokens = new ArrayList<String>(); 637 StringBuilder token = new StringBuilder (); 638 boolean prevIsEscapeChar = false; 639 for (int i = 0; i < s.length(); i += Character.charCount(i)) { 640 int currentChar = s.codePointAt(i); 641 if (prevIsEscapeChar) { 642 // Case 1: escaped character 643 token.appendCodePoint(currentChar); 644 prevIsEscapeChar = false; 645 } else if (currentChar == separator && tokens.size() < maxTokens-1) { 646 // Case 2: separator 647 tokens.add(token.toString()); 648 token = new StringBuilder(); 649 } else if (currentChar == '\\') { 650 // Case 3: escape character 651 prevIsEscapeChar = true; 652 } else { 653 // Case 4: regular character 654 token.appendCodePoint(currentChar); 655 } 656 } 657 if (token.length() > 0) { 658 tokens.add(token.toString()); 659 } 660 return tokens.toArray(new String[] {}); 661 } 662 addToSet(Set<String> s, String str)663 private void addToSet(Set<String> s, String str){ 664 StringTokenizer st = new StringTokenizer(str, ":"); 665 String current; 666 while(st.hasMoreTokens()){ 667 current = st.nextToken(); 668 s.add(current); 669 } 670 } 671 672 /** 673 * Add a trailing file separator, if not found. Remove superfluous 674 * file separators if any. Preserve the front double file separator for 675 * UNC paths. 676 * 677 * @param path Path under consideration. 678 * @return String Properly constructed path string. 679 */ addTrailingFileSep(String path)680 public static String addTrailingFileSep(String path) { 681 String fs = System.getProperty("file.separator"); 682 String dblfs = fs + fs; 683 int indexDblfs; 684 while ((indexDblfs = path.indexOf(dblfs, 1)) >= 0) { 685 path = path.substring(0, indexDblfs) + 686 path.substring(indexDblfs + fs.length()); 687 } 688 if (!path.endsWith(fs)) 689 path += fs; 690 return path; 691 } 692 693 /** 694 * This checks for the validity of the options used by the user. 695 * This works exactly like 696 * {@link com.sun.javadoc.Doclet#validOptions(String[][], 697 * DocErrorReporter)}. This will validate the options which are shared 698 * by our doclets. For example, this method will flag an error using 699 * the DocErrorReporter if user has used "-nohelp" and "-helpfile" option 700 * together. 701 * 702 * @param options options used on the command line. 703 * @param reporter used to report errors. 704 * @return true if all the options are valid. 705 */ generalValidOptions(String options[][], DocErrorReporter reporter)706 public boolean generalValidOptions(String options[][], 707 DocErrorReporter reporter) { 708 boolean docencodingfound = false; 709 String encoding = ""; 710 for (int oi = 0; oi < options.length; oi++) { 711 String[] os = options[oi]; 712 String opt = StringUtils.toLowerCase(os[0]); 713 if (opt.equals("-docencoding")) { 714 docencodingfound = true; 715 if (!checkOutputFileEncoding(os[1], reporter)) { 716 return false; 717 } 718 } else if (opt.equals("-encoding")) { 719 encoding = os[1]; 720 } 721 } 722 if (!docencodingfound && encoding.length() > 0) { 723 if (!checkOutputFileEncoding(encoding, reporter)) { 724 return false; 725 } 726 } 727 return true; 728 } 729 730 /** 731 * Check the validity of the given profile. Return false if there are no 732 * valid packages to be documented for the profile. 733 * 734 * @param profileName the profile that needs to be validated. 735 * @return true if the profile has valid packages to be documented. 736 */ shouldDocumentProfile(String profileName)737 public boolean shouldDocumentProfile(String profileName) { 738 return profilePackages.containsKey(profileName); 739 } 740 741 /** 742 * Check the validity of the given Source or Output File encoding on this 743 * platform. 744 * 745 * @param docencoding output file encoding. 746 * @param reporter used to report errors. 747 */ checkOutputFileEncoding(String docencoding, DocErrorReporter reporter)748 private boolean checkOutputFileEncoding(String docencoding, 749 DocErrorReporter reporter) { 750 OutputStream ost= new ByteArrayOutputStream(); 751 OutputStreamWriter osw = null; 752 try { 753 osw = new OutputStreamWriter(ost, docencoding); 754 } catch (UnsupportedEncodingException exc) { 755 reporter.printError(getText("doclet.Encoding_not_supported", 756 docencoding)); 757 return false; 758 } finally { 759 try { 760 if (osw != null) { 761 osw.close(); 762 } 763 } catch (IOException exc) { 764 } 765 } 766 return true; 767 } 768 769 /** 770 * Return true if the given doc-file subdirectory should be excluded and 771 * false otherwise. 772 * @param docfilesubdir the doc-files subdirectory to check. 773 */ shouldExcludeDocFileDir(String docfilesubdir)774 public boolean shouldExcludeDocFileDir(String docfilesubdir){ 775 if (excludedDocFileDirs.contains(docfilesubdir)) { 776 return true; 777 } else { 778 return false; 779 } 780 } 781 782 /** 783 * Return true if the given qualifier should be excluded and false otherwise. 784 * @param qualifier the qualifier to check. 785 */ shouldExcludeQualifier(String qualifier)786 public boolean shouldExcludeQualifier(String qualifier){ 787 if (excludedQualifiers.contains("all") || 788 excludedQualifiers.contains(qualifier) || 789 excludedQualifiers.contains(qualifier + ".*")) { 790 return true; 791 } else { 792 int index = -1; 793 while ((index = qualifier.indexOf(".", index + 1)) != -1) { 794 if (excludedQualifiers.contains(qualifier.substring(0, index + 1) + "*")) { 795 return true; 796 } 797 } 798 return false; 799 } 800 } 801 802 /** 803 * Return the qualified name of the <code>ClassDoc</code> if it's qualifier is not excluded. Otherwise, 804 * return the unqualified <code>ClassDoc</code> name. 805 * @param cd the <code>ClassDoc</code> to check. 806 */ getClassName(ClassDoc cd)807 public String getClassName(ClassDoc cd) { 808 PackageDoc pd = cd.containingPackage(); 809 if (pd != null && shouldExcludeQualifier(cd.containingPackage().name())) { 810 return cd.name(); 811 } else { 812 return cd.qualifiedName(); 813 } 814 } 815 getText(String key)816 public String getText(String key) { 817 try { 818 //Check the doclet specific properties file. 819 return getDocletSpecificMsg().getText(key); 820 } catch (Exception e) { 821 //Check the shared properties file. 822 return message.getText(key); 823 } 824 } 825 getText(String key, String a1)826 public String getText(String key, String a1) { 827 try { 828 //Check the doclet specific properties file. 829 return getDocletSpecificMsg().getText(key, a1); 830 } catch (Exception e) { 831 //Check the shared properties file. 832 return message.getText(key, a1); 833 } 834 } 835 getText(String key, String a1, String a2)836 public String getText(String key, String a1, String a2) { 837 try { 838 //Check the doclet specific properties file. 839 return getDocletSpecificMsg().getText(key, a1, a2); 840 } catch (Exception e) { 841 //Check the shared properties file. 842 return message.getText(key, a1, a2); 843 } 844 } 845 getText(String key, String a1, String a2, String a3)846 public String getText(String key, String a1, String a2, String a3) { 847 try { 848 //Check the doclet specific properties file. 849 return getDocletSpecificMsg().getText(key, a1, a2, a3); 850 } catch (Exception e) { 851 //Check the shared properties file. 852 return message.getText(key, a1, a2, a3); 853 } 854 } 855 newContent()856 public abstract Content newContent(); 857 858 /** 859 * Get the configuration string as a content. 860 * 861 * @param key the key to look for in the configuration file 862 * @return a content tree for the text 863 */ getResource(String key)864 public Content getResource(String key) { 865 Content c = newContent(); 866 c.addContent(getText(key)); 867 return c; 868 } 869 870 /** 871 * Get the configuration string as a content. 872 * 873 * @param key the key to look for in the configuration file 874 * @param o string or content argument added to configuration text 875 * @return a content tree for the text 876 */ getResource(String key, Object o)877 public Content getResource(String key, Object o) { 878 return getResource(key, o, null, null); 879 } 880 881 /** 882 * Get the configuration string as a content. 883 * 884 * @param key the key to look for in the configuration file 885 * @param o string or content argument added to configuration text 886 * @return a content tree for the text 887 */ getResource(String key, Object o1, Object o2)888 public Content getResource(String key, Object o1, Object o2) { 889 return getResource(key, o1, o2, null); 890 } 891 892 /** 893 * Get the configuration string as a content. 894 * 895 * @param key the key to look for in the configuration file 896 * @param o1 string or content argument added to configuration text 897 * @param o2 string or content argument added to configuration text 898 * @return a content tree for the text 899 */ getResource(String key, Object o0, Object o1, Object o2)900 public Content getResource(String key, Object o0, Object o1, Object o2) { 901 Content c = newContent(); 902 Pattern p = Pattern.compile("\\{([012])\\}"); 903 String text = getText(key); 904 Matcher m = p.matcher(text); 905 int start = 0; 906 while (m.find(start)) { 907 c.addContent(text.substring(start, m.start())); 908 909 Object o = null; 910 switch (m.group(1).charAt(0)) { 911 case '0': o = o0; break; 912 case '1': o = o1; break; 913 case '2': o = o2; break; 914 } 915 916 if (o == null) { 917 c.addContent("{" + m.group(1) + "}"); 918 } else if (o instanceof String) { 919 c.addContent((String) o); 920 } else if (o instanceof Content) { 921 c.addContent((Content) o); 922 } 923 924 start = m.end(); 925 } 926 927 c.addContent(text.substring(start)); 928 return c; 929 } 930 931 932 /** 933 * Return true if the ClassDoc element is getting documented, depending upon 934 * -nodeprecated option and the deprecation information. Return true if 935 * -nodeprecated is not used. Return false if -nodeprecated is used and if 936 * either ClassDoc element is deprecated or the containing package is deprecated. 937 * 938 * @param cd the ClassDoc for which the page generation is checked 939 */ isGeneratedDoc(ClassDoc cd)940 public boolean isGeneratedDoc(ClassDoc cd) { 941 if (!nodeprecated) { 942 return true; 943 } 944 return !(Util.isDeprecated(cd) || Util.isDeprecated(cd.containingPackage())); 945 } 946 947 /** 948 * Return the doclet specific instance of a writer factory. 949 * @return the {@link WriterFactory} for the doclet. 950 */ getWriterFactory()951 public abstract WriterFactory getWriterFactory(); 952 953 /** 954 * Return the input stream to the builder XML. 955 * 956 * @return the input steam to the builder XML. 957 * @throws FileNotFoundException when the given XML file cannot be found. 958 */ getBuilderXML()959 public InputStream getBuilderXML() throws IOException { 960 return builderXMLPath == null ? 961 Configuration.class.getResourceAsStream(DEFAULT_BUILDER_XML) : 962 DocFile.createFileForInput(this, builderXMLPath).openInputStream(); 963 } 964 965 /** 966 * Return the Locale for this document. 967 */ getLocale()968 public abstract Locale getLocale(); 969 970 /** 971 * Return the current file manager. 972 */ getFileManager()973 public abstract JavaFileManager getFileManager(); 974 975 /** 976 * Return the comparator that will be used to sort member documentation. 977 * To no do any sorting, return null. 978 * 979 * @return the {@link java.util.Comparator} used to sort members. 980 */ getMemberComparator()981 public abstract Comparator<ProgramElementDoc> getMemberComparator(); 982 setTabWidth(int n)983 private void setTabWidth(int n) { 984 sourcetab = n; 985 tabSpaces = String.format("%" + n + "s", ""); 986 } 987 showMessage(SourcePosition pos, String key)988 public abstract boolean showMessage(SourcePosition pos, String key); 989 } 990