Tue, 09 Oct 2012 19:31:58 -0700
8000208: fix langtools javadoc comment issues
Reviewed-by: bpatel, mcimadamore
1 /*
2 * Copyright (c) 1997, 2012, 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;
28 import java.io.*;
29 import java.util.*;
31 import com.sun.javadoc.*;
32 import com.sun.tools.doclets.internal.toolkit.builders.BuilderFactory;
33 import com.sun.tools.doclets.internal.toolkit.taglets.*;
34 import com.sun.tools.doclets.internal.toolkit.util.*;
36 /**
37 * Configure the output based on the options. Doclets should sub-class
38 * Configuration, to configure and add their own options. This class contains
39 * all user options which are supported by the 1.1 doclet and the standard
40 * doclet.
41 *
42 * This code is not part of an API.
43 * It is implementation that is subject to change.
44 * Do not use it as an API
45 *
46 * @author Robert Field.
47 * @author Atul Dambalkar.
48 * @author Jamie Ho
49 */
50 public abstract class Configuration {
52 /**
53 * The factory for builders.
54 */
55 protected BuilderFactory builderFactory;
57 /**
58 * The taglet manager.
59 */
60 public TagletManager tagletManager;
62 /**
63 * The path to the builder XML input file.
64 */
65 public String builderXMLPath;
67 /**
68 * The default path to the builder XML.
69 */
70 private static final String DEFAULT_BUILDER_XML = "resources/doclet.xml";
72 /**
73 * The path to Taglets
74 */
75 public String tagletpath = "";
77 /**
78 * This is true if option "-serialwarn" is used. Defualt value is false to
79 * supress excessive warnings about serial tag.
80 */
81 public boolean serialwarn = false;
83 /**
84 * The specified amount of space between tab stops.
85 */
86 public int sourcetab = DocletConstants.DEFAULT_TAB_STOP_LENGTH;
88 /**
89 * True if we should generate browsable sources.
90 */
91 public boolean linksource = false;
93 /**
94 * True if command line option "-nosince" is used. Default value is
95 * false.
96 */
97 public boolean nosince = false;
99 /**
100 * True if we should recursively copy the doc-file subdirectories
101 */
102 public boolean copydocfilesubdirs = false;
104 /**
105 * The META charset tag used for cross-platform viewing.
106 */
107 public String charset = "";
109 /**
110 * True if user wants to add member names as meta keywords.
111 * Set to false because meta keywords are ignored in general
112 * by most Internet search engines.
113 */
114 public boolean keywords = false;
116 /**
117 * The meta tag keywords instance.
118 */
119 public final MetaKeywords metakeywords = new MetaKeywords(this);
121 /**
122 * The list of doc-file subdirectories to exclude
123 */
124 protected Set<String> excludedDocFileDirs;
126 /**
127 * The list of qualifiers to exclude
128 */
129 protected Set<String> excludedQualifiers;
131 /**
132 * The Root of the generated Program Structure from the Doclet API.
133 */
134 public RootDoc root;
136 /**
137 * Destination directory name, in which doclet will generate the entire
138 * documentation. Default is current directory.
139 */
140 public String destDirName = "";
142 /**
143 * Destination directory name, in which doclet will copy the doc-files to.
144 */
145 public String docFileDestDirName = "";
147 /**
148 * Encoding for this document. Default is default encoding for this
149 * platform.
150 */
151 public String docencoding = null;
153 /**
154 * True if user wants to suppress descriptions and tags.
155 */
156 public boolean nocomment = false;
158 /**
159 * Encoding for this document. Default is default encoding for this
160 * platform.
161 */
162 public String encoding = null;
164 /**
165 * Generate author specific information for all the classes if @author
166 * tag is used in the doc comment and if -author option is used.
167 * <code>showauthor</code> is set to true if -author option is used.
168 * Default is don't show author information.
169 */
170 public boolean showauthor = false;
172 /**
173 * Generate version specific information for the all the classes
174 * if @version tag is used in the doc comment and if -version option is
175 * used. <code>showversion</code> is set to true if -version option is
176 * used.Default is don't show version information.
177 */
178 public boolean showversion = false;
180 /**
181 * Sourcepath from where to read the source files. Default is classpath.
182 *
183 */
184 public String sourcepath = "";
186 /**
187 * Don't generate deprecated API information at all, if -nodeprecated
188 * option is used. <code>nodepracted</code> is set to true if
189 * -nodeprecated option is used. Default is generate deprected API
190 * information.
191 */
192 public boolean nodeprecated = false;
194 /**
195 * The catalog of classes specified on the command-line
196 */
197 public ClassDocCatalog classDocCatalog;
199 /**
200 * Message Retriever for the doclet, to retrieve message from the resource
201 * file for this Configuration, which is common for 1.1 and standard
202 * doclets.
203 *
204 * TODO: Make this private!!!
205 */
206 public MessageRetriever message = null;
208 /**
209 * True if user wants to suppress time stamp in output.
210 * Default is false.
211 */
212 public boolean notimestamp= false;
214 /**
215 * The package grouping instance.
216 */
217 public final Group group = new Group(this);
219 /**
220 * The tracker of external package links.
221 */
222 public final Extern extern = new Extern(this);
224 /**
225 * Return the build date for the doclet.
226 */
227 public abstract String getDocletSpecificBuildDate();
229 /**
230 * This method should be defined in all those doclets(configurations),
231 * which want to derive themselves from this Configuration. This method
232 * can be used to set its own command line options.
233 *
234 * @param options The array of option names and values.
235 * @throws DocletAbortException
236 */
237 public abstract void setSpecificDocletOptions(String[][] options);
239 /**
240 * Return the doclet specific {@link MessageRetriever}
241 * @return the doclet specific MessageRetriever.
242 */
243 public abstract MessageRetriever getDocletSpecificMsg();
245 /**
246 * An array of the packages specified on the command-line merged
247 * with the array of packages that contain the classes specified on the
248 * command-line. The array is sorted.
249 */
250 public PackageDoc[] packages;
252 /**
253 * Constructor. Constructs the message retriever with resource file.
254 */
255 public Configuration() {
256 message =
257 new MessageRetriever(this,
258 "com.sun.tools.doclets.internal.toolkit.resources.doclets");
259 excludedDocFileDirs = new HashSet<String>();
260 excludedQualifiers = new HashSet<String>();
261 }
263 /**
264 * Return the builder factory for this doclet.
265 *
266 * @return the builder factory for this doclet.
267 */
268 public BuilderFactory getBuilderFactory() {
269 if (builderFactory == null) {
270 builderFactory = new BuilderFactory(this);
271 }
272 return builderFactory;
273 }
275 /**
276 * This method should be defined in all those doclets
277 * which want to inherit from this Configuration. This method
278 * should return the number of arguments to the command line
279 * option (including the option name). For example,
280 * -notimestamp is a single-argument option, so this method would
281 * return 1.
282 *
283 * @param option Command line option under consideration.
284 * @return number of arguments to option (including the
285 * option name). Zero return means option not known.
286 * Negative value means error occurred.
287 */
288 public int optionLength(String option) {
289 option = option.toLowerCase();
290 if (option.equals("-author") ||
291 option.equals("-docfilessubdirs") ||
292 option.equals("-keywords") ||
293 option.equals("-linksource") ||
294 option.equals("-nocomment") ||
295 option.equals("-nodeprecated") ||
296 option.equals("-nosince") ||
297 option.equals("-notimestamp") ||
298 option.equals("-quiet") ||
299 option.equals("-xnodate") ||
300 option.equals("-version")) {
301 return 1;
302 } else if (option.equals("-d") ||
303 option.equals("-docencoding") ||
304 option.equals("-encoding") ||
305 option.equals("-excludedocfilessubdir") ||
306 option.equals("-link") ||
307 option.equals("-sourcetab") ||
308 option.equals("-noqualifier") ||
309 option.equals("-output") ||
310 option.equals("-sourcepath") ||
311 option.equals("-tag") ||
312 option.equals("-taglet") ||
313 option.equals("-tagletpath")) {
314 return 2;
315 } else if (option.equals("-group") ||
316 option.equals("-linkoffline")) {
317 return 3;
318 } else {
319 return -1; // indicate we don't know about it
320 }
321 }
323 /**
324 * Perform error checking on the given options.
325 *
326 * @param options the given options to check.
327 * @param reporter the reporter used to report errors.
328 */
329 public abstract boolean validOptions(String options[][],
330 DocErrorReporter reporter);
332 private void initPackageArray() {
333 Set<PackageDoc> set = new HashSet<PackageDoc>(Arrays.asList(root.specifiedPackages()));
334 ClassDoc[] classes = root.specifiedClasses();
335 for (int i = 0; i < classes.length; i++) {
336 set.add(classes[i].containingPackage());
337 }
338 ArrayList<PackageDoc> results = new ArrayList<PackageDoc>(set);
339 Collections.sort(results);
340 packages = results.toArray(new PackageDoc[] {});
341 }
343 /**
344 * Set the command line options supported by this configuration.
345 *
346 * @param options the two dimensional array of options.
347 */
348 public void setOptions(String[][] options) {
349 LinkedHashSet<String[]> customTagStrs = new LinkedHashSet<String[]>();
350 for (int oi = 0; oi < options.length; ++oi) {
351 String[] os = options[oi];
352 String opt = os[0].toLowerCase();
353 if (opt.equals("-d")) {
354 destDirName = addTrailingFileSep(os[1]);
355 docFileDestDirName = destDirName;
356 } else if (opt.equals("-docfilessubdirs")) {
357 copydocfilesubdirs = true;
358 } else if (opt.equals("-docencoding")) {
359 docencoding = os[1];
360 } else if (opt.equals("-encoding")) {
361 encoding = os[1];
362 } else if (opt.equals("-author")) {
363 showauthor = true;
364 } else if (opt.equals("-nosince")) {
365 nosince = true;
366 } else if (opt.equals("-version")) {
367 showversion = true;
368 } else if (opt.equals("-nodeprecated")) {
369 nodeprecated = true;
370 } else if (opt.equals("-sourcepath")) {
371 sourcepath = os[1];
372 } else if (opt.equals("-classpath") &&
373 sourcepath.length() == 0) {
374 sourcepath = os[1];
375 } else if (opt.equals("-excludedocfilessubdir")) {
376 addToSet(excludedDocFileDirs, os[1]);
377 } else if (opt.equals("-noqualifier")) {
378 addToSet(excludedQualifiers, os[1]);
379 } else if (opt.equals("-linksource")) {
380 linksource = true;
381 } else if (opt.equals("-sourcetab")) {
382 linksource = true;
383 try {
384 sourcetab = Integer.parseInt(os[1]);
385 } catch (NumberFormatException e) {
386 //Set to -1 so that warning will be printed
387 //to indicate what is valid argument.
388 sourcetab = -1;
389 }
390 if (sourcetab <= 0) {
391 message.warning("doclet.sourcetab_warning");
392 sourcetab = DocletConstants.DEFAULT_TAB_STOP_LENGTH;
393 }
394 } else if (opt.equals("-notimestamp")) {
395 notimestamp = true;
396 } else if (opt.equals("-nocomment")) {
397 nocomment = true;
398 } else if (opt.equals("-tag") || opt.equals("-taglet")) {
399 customTagStrs.add(os);
400 } else if (opt.equals("-tagletpath")) {
401 tagletpath = os[1];
402 } else if (opt.equals("-keywords")) {
403 keywords = true;
404 } else if (opt.equals("-serialwarn")) {
405 serialwarn = true;
406 } else if (opt.equals("-group")) {
407 group.checkPackageGroups(os[1], os[2]);
408 } else if (opt.equals("-link")) {
409 String url = os[1];
410 extern.url(url, url, root, false);
411 } else if (opt.equals("-linkoffline")) {
412 String url = os[1];
413 String pkglisturl = os[2];
414 extern.url(url, pkglisturl, root, true);
415 }
416 }
417 if (sourcepath.length() == 0) {
418 sourcepath = System.getProperty("env.class.path") == null ? "" :
419 System.getProperty("env.class.path");
420 }
421 if (docencoding == null) {
422 docencoding = encoding;
423 }
425 classDocCatalog = new ClassDocCatalog(root.specifiedClasses(), this);
426 initTagletManager(customTagStrs);
427 }
429 /**
430 * Set the command line options supported by this configuration.
431 *
432 * @throws DocletAbortException
433 */
434 public void setOptions() {
435 initPackageArray();
436 setOptions(root.options());
437 setSpecificDocletOptions(root.options());
438 }
441 /**
442 * Initialize the taglet manager. The strings to initialize the simple custom tags should
443 * be in the following format: "[tag name]:[location str]:[heading]".
444 * @param customTagStrs the set two dimentional arrays of strings. These arrays contain
445 * either -tag or -taglet arguments.
446 */
447 private void initTagletManager(Set<String[]> customTagStrs) {
448 tagletManager = tagletManager == null ?
449 new TagletManager(nosince, showversion, showauthor, message) :
450 tagletManager;
451 String[] args;
452 for (Iterator<String[]> it = customTagStrs.iterator(); it.hasNext(); ) {
453 args = it.next();
454 if (args[0].equals("-taglet")) {
455 tagletManager.addCustomTag(args[1], tagletpath);
456 continue;
457 }
458 String[] tokens = Util.tokenize(args[1],
459 TagletManager.SIMPLE_TAGLET_OPT_SEPERATOR, 3);
460 if (tokens.length == 1) {
461 String tagName = args[1];
462 if (tagletManager.isKnownCustomTag(tagName)) {
463 //reorder a standard tag
464 tagletManager.addNewSimpleCustomTag(tagName, null, "");
465 } else {
466 //Create a simple tag with the heading that has the same name as the tag.
467 StringBuffer heading = new StringBuffer(tagName + ":");
468 heading.setCharAt(0, Character.toUpperCase(tagName.charAt(0)));
469 tagletManager.addNewSimpleCustomTag(tagName, heading.toString(), "a");
470 }
471 } else if (tokens.length == 2) {
472 //Add simple taglet without heading, probably to excluding it in the output.
473 tagletManager.addNewSimpleCustomTag(tokens[0], tokens[1], "");
474 } else if (tokens.length >= 3) {
475 tagletManager.addNewSimpleCustomTag(tokens[0], tokens[2], tokens[1]);
476 } else {
477 message.error("doclet.Error_invalid_custom_tag_argument", args[1]);
478 }
479 }
480 }
482 private void addToSet(Set<String> s, String str){
483 StringTokenizer st = new StringTokenizer(str, ":");
484 String current;
485 while(st.hasMoreTokens()){
486 current = st.nextToken();
487 s.add(current);
488 }
489 }
491 /**
492 * Add a trailing file separator, if not found. Remove superfluous
493 * file separators if any. Preserve the front double file separator for
494 * UNC paths.
495 *
496 * @param path Path under consideration.
497 * @return String Properly constructed path string.
498 */
499 public static String addTrailingFileSep(String path) {
500 String fs = System.getProperty("file.separator");
501 String dblfs = fs + fs;
502 int indexDblfs;
503 while ((indexDblfs = path.indexOf(dblfs, 1)) >= 0) {
504 path = path.substring(0, indexDblfs) +
505 path.substring(indexDblfs + fs.length());
506 }
507 if (!path.endsWith(fs))
508 path += fs;
509 return path;
510 }
512 /**
513 * This checks for the validity of the options used by the user.
514 * This works exactly like
515 * {@link com.sun.javadoc.Doclet#validOptions(String[][],
516 * DocErrorReporter)}. This will validate the options which are shared
517 * by our doclets. For example, this method will flag an error using
518 * the DocErrorReporter if user has used "-nohelp" and "-helpfile" option
519 * together.
520 *
521 * @param options options used on the command line.
522 * @param reporter used to report errors.
523 * @return true if all the options are valid.
524 */
525 public boolean generalValidOptions(String options[][],
526 DocErrorReporter reporter) {
527 boolean docencodingfound = false;
528 String encoding = "";
529 for (int oi = 0; oi < options.length; oi++) {
530 String[] os = options[oi];
531 String opt = os[0].toLowerCase();
532 if (opt.equals("-d")) {
533 String destdirname = addTrailingFileSep(os[1]);
534 File destDir = new File(destdirname);
535 if (!destDir.exists()) {
536 //Create the output directory (in case it doesn't exist yet)
537 reporter.printNotice(getText("doclet.dest_dir_create",
538 destdirname));
539 (new File(destdirname)).mkdirs();
540 } else if (!destDir.isDirectory()) {
541 reporter.printError(getText(
542 "doclet.destination_directory_not_directory_0",
543 destDir.getPath()));
544 return false;
545 } else if (!destDir.canWrite()) {
546 reporter.printError(getText(
547 "doclet.destination_directory_not_writable_0",
548 destDir.getPath()));
549 return false;
550 }
551 } else if (opt.equals("-docencoding")) {
552 docencodingfound = true;
553 if (!checkOutputFileEncoding(os[1], reporter)) {
554 return false;
555 }
556 } else if (opt.equals("-encoding")) {
557 encoding = os[1];
558 }
559 }
560 if (!docencodingfound && encoding.length() > 0) {
561 if (!checkOutputFileEncoding(encoding, reporter)) {
562 return false;
563 }
564 }
565 return true;
566 }
568 /**
569 * Check the validity of the given Source or Output File encoding on this
570 * platform.
571 *
572 * @param docencoding output file encoding.
573 * @param reporter used to report errors.
574 */
575 private boolean checkOutputFileEncoding(String docencoding,
576 DocErrorReporter reporter) {
577 OutputStream ost= new ByteArrayOutputStream();
578 OutputStreamWriter osw = null;
579 try {
580 osw = new OutputStreamWriter(ost, docencoding);
581 } catch (UnsupportedEncodingException exc) {
582 reporter.printError(getText("doclet.Encoding_not_supported",
583 docencoding));
584 return false;
585 } finally {
586 try {
587 if (osw != null) {
588 osw.close();
589 }
590 } catch (IOException exc) {
591 }
592 }
593 return true;
594 }
596 /**
597 * Return true if the given doc-file subdirectory should be excluded and
598 * false otherwise.
599 * @param docfilesubdir the doc-files subdirectory to check.
600 */
601 public boolean shouldExcludeDocFileDir(String docfilesubdir){
602 if (excludedDocFileDirs.contains(docfilesubdir)) {
603 return true;
604 } else {
605 return false;
606 }
607 }
609 /**
610 * Return true if the given qualifier should be excluded and false otherwise.
611 * @param qualifier the qualifier to check.
612 */
613 public boolean shouldExcludeQualifier(String qualifier){
614 if (excludedQualifiers.contains("all") ||
615 excludedQualifiers.contains(qualifier) ||
616 excludedQualifiers.contains(qualifier + ".*")) {
617 return true;
618 } else {
619 int index = -1;
620 while ((index = qualifier.indexOf(".", index + 1)) != -1) {
621 if (excludedQualifiers.contains(qualifier.substring(0, index + 1) + "*")) {
622 return true;
623 }
624 }
625 return false;
626 }
627 }
629 /**
630 * Return the qualified name of the <code>ClassDoc</code> if it's qualifier is not excluded. Otherwise,
631 * return the unqualified <code>ClassDoc</code> name.
632 * @param cd the <code>ClassDoc</code> to check.
633 */
634 public String getClassName(ClassDoc cd) {
635 PackageDoc pd = cd.containingPackage();
636 if (pd != null && shouldExcludeQualifier(cd.containingPackage().name())) {
637 return cd.name();
638 } else {
639 return cd.qualifiedName();
640 }
641 }
643 public String getText(String key) {
644 try {
645 //Check the doclet specific properties file.
646 return getDocletSpecificMsg().getText(key);
647 } catch (Exception e) {
648 //Check the shared properties file.
649 return message.getText(key);
650 }
651 }
653 public String getText(String key, String a1) {
654 try {
655 //Check the doclet specific properties file.
656 return getDocletSpecificMsg().getText(key, a1);
657 } catch (Exception e) {
658 //Check the shared properties file.
659 return message.getText(key, a1);
660 }
661 }
663 public String getText(String key, String a1, String a2) {
664 try {
665 //Check the doclet specific properties file.
666 return getDocletSpecificMsg().getText(key, a1, a2);
667 } catch (Exception e) {
668 //Check the shared properties file.
669 return message.getText(key, a1, a2);
670 }
671 }
673 public String getText(String key, String a1, String a2, String a3) {
674 try {
675 //Check the doclet specific properties file.
676 return getDocletSpecificMsg().getText(key, a1, a2, a3);
677 } catch (Exception e) {
678 //Check the shared properties file.
679 return message.getText(key, a1, a2, a3);
680 }
681 }
683 /**
684 * Return true if the ClassDoc element is getting documented, depending upon
685 * -nodeprecated option and the deprecation information. Return true if
686 * -nodeprecated is not used. Return false if -nodeprecated is used and if
687 * either ClassDoc element is deprecated or the containing package is deprecated.
688 *
689 * @param cd the ClassDoc for which the page generation is checked
690 */
691 public boolean isGeneratedDoc(ClassDoc cd) {
692 if (!nodeprecated) {
693 return true;
694 }
695 return !(Util.isDeprecated(cd) || Util.isDeprecated(cd.containingPackage()));
696 }
698 /**
699 * Return the doclet specific instance of a writer factory.
700 * @return the {@link WriterFactory} for the doclet.
701 */
702 public abstract WriterFactory getWriterFactory();
704 /**
705 * Return the input stream to the builder XML.
706 *
707 * @return the input steam to the builder XML.
708 * @throws FileNotFoundException when the given XML file cannot be found.
709 */
710 public InputStream getBuilderXML() throws FileNotFoundException {
711 return builderXMLPath == null ?
712 Configuration.class.getResourceAsStream(DEFAULT_BUILDER_XML) :
713 new FileInputStream(new File(builderXMLPath));
714 }
716 /**
717 * Return the Locale for this document.
718 */
719 public abstract Locale getLocale();
721 /**
722 * Return the comparator that will be used to sort member documentation.
723 * To no do any sorting, return null.
724 *
725 * @return the {@link java.util.Comparator} used to sort members.
726 */
727 public abstract Comparator<ProgramElementDoc> getMemberComparator();
728 }