1 /* gnu.classpath.tools.gjdoc.Main
2 Copyright (C) 2001 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 package gnu
.classpath
.tools
.gjdoc
;
23 import com
.sun
.javadoc
.*;
26 import java
.lang
.reflect
.*;
27 import java
.text
.Collator
;
29 import gnu
.classpath
.tools
.FileSystemClassLoader
;
32 * Class that will launch the gjdoc tool.
34 public final class Main
38 * Do we load classes that are referenced as base class?
40 static final boolean DESCEND_SUPERCLASS
= true;
43 * Do we load classes that are referenced as interface?
45 static final boolean DESCEND_INTERFACES
= false;
48 * Do we load classes that are imported in a source file?
50 static final boolean DESCEND_IMPORTED
= true;
53 * Document only public members.
55 static final int COVERAGE_PUBLIC
= 0;
58 * Document only public and protected members.
60 static final int COVERAGE_PROTECTED
= 1;
63 * Document public, protected and package private members.
65 static final int COVERAGE_PACKAGE
= 2;
68 * Document all members.
70 static final int COVERAGE_PRIVATE
= 3;
73 * FIXME: This should come from a ResourceBundle
75 private static final String STRING_TRY_GJDOC_HELP
=
76 "Try `gjdoc --help' for more information.";
79 * Grid for looking up whether a particular access level is included in the
82 static final boolean[][] coverageTemplates
= new boolean[][]
84 { true, false, false, false }, // public
86 { true, true, false, false }, // protected
88 { true, true, true, false }, // package
90 { true, true, true, true }, // private
94 * Holds the Singleton instance of this class.
96 private static Main instance
= new Main();
99 * Avoid re-instantiation of this class.
105 private static RootDocImpl rootDoc
;
107 private ErrorReporter reporter
;
110 * Cache for version string from resource /version.properties
112 private String gjdocVersion
;
115 * <code>false</code> during Phase I: preparation of the documentation data.
116 * <code>true</code> during Phase II: documentation output by doclet.
118 boolean docletRunning
= false;
120 //---- Command line options
123 * Option "-doclet": name of the Doclet class to use.
125 private String option_doclet
= "gnu.classpath.tools.doclets.htmldoclet.HtmlDoclet";
128 * Option "-overview": path to the special overview file.
130 private String option_overview
;
133 * Option "-coverage": which members to include in generated documentation.
135 private int option_coverage
= COVERAGE_PROTECTED
;
138 * Option "-help": display command line usage.
140 private boolean option_help
;
143 * Option "-docletpath": path to doclet classes.
145 private String option_docletpath
;
148 * Option "-classpath": path to additional classes.
150 private String option_classpath
;
153 * Option "-sourcepath": path to the Java source files to be documented.
154 * FIXME: this should be a list of paths
156 private List option_sourcepath
= new ArrayList();
159 * Option "-extdirs": path to Java extension files.
161 private String option_extdirs
;
164 * Option "-verbose": Be verbose when generating documentation.
166 private boolean option_verbose
;
169 * Option "-nowarn": Do not print warnings.
171 private boolean option_nowarn
;
174 * Option "-locale:" Specify the locale charset of Java source files.
176 private Locale option_locale
= new Locale("en", "us");
179 * Option "-encoding": Specify character encoding of Java source files.
181 private String option_encoding
;
184 * Option "-J": Specify flags to be passed to Java runtime.
186 private List option_java_flags
= new LinkedList(); //ArrayList();
189 * Option "-source:" should be 1.4 to handle assertions, 1.1 is no
192 private String option_source
= "1.2";
195 * Option "-subpackages": list of subpackages to be recursively
198 private List option_subpackages
= new ArrayList();
201 * Option "-exclude": list of subpackages to exclude.
203 private List option_exclude
= new ArrayList();
206 * Option "-breakiterator" - whether to use BreakIterator for
207 * detecting the end of the first sentence.
209 private boolean option_breakiterator
;
212 * Option "-licensetext" - whether to copy license text.
214 private boolean option_licensetext
;
217 * The locale-dependent collator used for sorting.
219 private Collator collator
;
222 * true when --version has been specified on the command line.
224 private boolean option_showVersion
;
227 * true when -bootclasspath has been specified on the command line.
229 private boolean option_bootclasspath_specified
;
232 * true when -all has been specified on the command line.
234 private boolean option_all
;
237 * true when -reflection has been specified on the command line.
239 private boolean option_reflection
;
241 // TODO: add the rest of the options as instance variables
244 * Parse all source files/packages and subsequentially start the Doclet given
245 * on the command line.
247 * @param allOptions List of all command line tokens
249 private boolean startDoclet(List allOptions
)
255 //--- Fetch the Class object for the Doclet.
257 Debug
.log(1, "loading doclet class...");
261 if (null != option_docletpath
) {
263 FileSystemClassLoader docletPathClassLoader
264 = new FileSystemClassLoader(option_docletpath
);
265 System
.err
.println("trying to load class " + option_doclet
+ " from path " + option_docletpath
);
266 docletClass
= docletPathClassLoader
.findClass(option_doclet
);
268 catch (Exception e
) {
269 docletClass
= Class
.forName(option_doclet
);
273 docletClass
= Class
.forName(option_doclet
);
275 //Object docletInstance = docletClass.newInstance();
277 Debug
.log(1, "doclet class loaded...");
279 Method startTempMethod
= null;
280 Method startMethod
= null;
281 Method optionLenMethod
= null;
282 Method validOptionsMethod
= null;
284 //--- Try to find the optionLength method in the Doclet class.
288 optionLenMethod
= docletClass
.getMethod("optionLength", new Class
[]
291 catch (NoSuchMethodException e
)
293 // Ignore if not found; it's OK it the Doclet class doesn't define
297 //--- Try to find the validOptions method in the Doclet class.
301 validOptionsMethod
= docletClass
.getMethod("validOptions", new Class
[]
302 { String
[][].class, DocErrorReporter
.class });
304 catch (NoSuchMethodException e
)
306 // Ignore if not found; it's OK it the Doclet class doesn't define
310 //--- Find the start method in the Doclet class; complain if not found
314 startTempMethod
= docletClass
.getMethod("start", new Class
[]
315 { TemporaryStore
.class });
321 startMethod
= docletClass
.getMethod("start", new Class
[]
324 //--- Feed the custom command line tokens to the Doclet
326 // stores all recognized options
327 List options
= new LinkedList();
329 // stores packages and classes defined on the command line
330 List packageAndClasses
= new LinkedList();
332 for (Iterator it
= allOptions
.iterator(); it
.hasNext();)
334 String option
= (String
) it
.next();
336 Debug
.log(9, "parsing option '" + option
+ "'");
338 if (option
.startsWith("-"))
343 int optlen
= optionLength(option
);
345 //--- Try to get option length from Doclet class
347 if (optlen
<= 0 && optionLenMethod
!= null)
350 optionLenMethod
.invoke(null, new Object
[]
353 Debug
.log(3, "invoking optionLen method");
355 optlen
= ((Integer
) optionLenMethod
.invoke(null, new Object
[]
356 { option
})).intValue();
358 Debug
.log(3, "done");
363 if (option
.startsWith("-JD")) {
364 // Simulate VM option -D
365 String propertyValue
= option
.substring(3);
366 int ndx
= propertyValue
.indexOf('=');
368 reporter
.printError("Illegal format in option " + option
+ ": use -JDproperty=value");
372 String property
= propertyValue
.substring(0, ndx
);
373 String value
= propertyValue
.substring(ndx
+ 1);
374 System
.setProperty(property
, value
);
377 else if (option
.startsWith("-J")) {
378 //--- Warn if VM option is encountered
379 reporter
.printWarning("Ignored option " + option
+ ". Pass this option to the VM if required.");
382 //--- Complain if not found
384 reporter
.printError("Unknown option " + option
);
385 reporter
.printNotice(STRING_TRY_GJDOC_HELP
);
392 //--- Read option values
394 String
[] optionAndValues
= new String
[optlen
];
395 optionAndValues
[0] = option
;
396 for (int i
= 1; i
< optlen
; ++i
)
400 reporter
.printError("Missing value for option " + option
);
405 optionAndValues
[i
] = (String
) it
.next();
409 //--- Store option for processing later
411 options
.add(optionAndValues
);
414 else if (option
.length() > 0)
417 //--- Add to list of packages/classes if not option or option
420 packageAndClasses
.add(option
);
424 Debug
.log(9, "options parsed...");
426 //--- For each package specified with the -subpackages option on
427 // the command line, recursively find all valid java files
430 //--- For each class or package specified on the command line,
431 // check that it exists and find out whether it is a class
434 for (Iterator it
= option_subpackages
.iterator(); it
.hasNext();)
436 String subpackage
= (String
) it
.next();
437 Set foundPackages
= new LinkedHashSet();
439 for (Iterator pit
= option_sourcepath
.iterator(); pit
.hasNext(); ) {
440 File sourceDir
= (File
)pit
.next();
441 File packageDir
= new File(sourceDir
, subpackage
.replace('.', File
.separatorChar
));
442 findPackages(subpackage
, packageDir
, foundPackages
);
445 addFoundPackages(subpackage
, foundPackages
);
449 Set foundPackages
= new LinkedHashSet();
450 for (Iterator pit
= option_sourcepath
.iterator(); pit
.hasNext(); ) {
451 File sourceDir
= (File
)pit
.next();
452 findPackages("", sourceDir
, foundPackages
);
454 addFoundPackages(null, foundPackages
);
455 for (Iterator packageIt
= foundPackages
.iterator(); packageIt
.hasNext(); ) {
456 String packageName
= (String
)packageIt
.next();
457 if (null == packageName
) {
460 rootDoc
.addSpecifiedPackageName(packageName
);
464 for (Iterator it
= packageAndClasses
.iterator(); it
.hasNext();)
467 String classOrPackage
= (String
) it
.next();
469 boolean foundSourceFile
= false;
471 if (classOrPackage
.endsWith(".java")) {
472 for (Iterator pit
= option_sourcepath
.iterator(); pit
.hasNext() && !foundSourceFile
; ) {
473 File sourceDir
= (File
)pit
.next();
474 File sourceFile
= new File(sourceDir
, classOrPackage
);
475 if (sourceFile
.exists() && !sourceFile
.isDirectory()) {
476 rootDoc
.addSpecifiedSourceFile(sourceFile
);
477 foundSourceFile
= true;
481 if (!foundSourceFile
) {
482 File sourceFile
= new File(classOrPackage
);
483 if (sourceFile
.exists() && !sourceFile
.isDirectory()) {
484 rootDoc
.addSpecifiedSourceFile(sourceFile
);
485 foundSourceFile
= true;
490 if (!foundSourceFile
) {
491 //--- Check for illegal name
493 if (classOrPackage
.startsWith(".")
494 || classOrPackage
.endsWith(".")
495 || classOrPackage
.indexOf("..") > 0
496 || !checkCharSet(classOrPackage
,
497 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_."))
499 throw new ParseException("Illegal class or package name '"
500 + classOrPackage
+ "'");
503 //--- Assemble absolute path to package
505 String classOrPackageRelPath
= classOrPackage
.replace('.',
508 //--- Create one file object each for a possible package directory
509 // and a possible class file, and find out if they exist.
511 List packageDirs
= rootDoc
.findSourceFiles(classOrPackageRelPath
);
512 List sourceFiles
= rootDoc
.findSourceFiles(classOrPackageRelPath
+ ".java");
514 boolean packageDirExists
= !packageDirs
.isEmpty();
515 boolean sourceFileExists
= !sourceFiles
.isEmpty();
517 //--- Complain if neither exists: not found
519 if (!packageDirExists
&& !sourceFileExists
)
521 reporter
.printError("Class or package " + classOrPackage
526 //--- Complain if both exist: ambigious
529 if (packageDirExists
&& sourceFileExists
)
531 reporter
.printError("Ambigious class/package name "
532 + classOrPackage
+ ".");
536 //--- Otherwise, if the package directory exists, it is a package
539 if (packageDirExists
) {
540 Iterator packageDirIt
= packageDirs
.iterator();
541 boolean packageDirFound
= false;
542 while (packageDirIt
.hasNext()) {
543 File packageDir
= (File
)packageDirIt
.next();
544 if (packageDir
.isDirectory()) {
545 rootDoc
.addSpecifiedPackageName(classOrPackage
);
546 packageDirFound
= true;
550 if (!packageDirFound
) {
551 reporter
.printError("No suitable file or directory found for" + classOrPackage
);
556 //--- Otherwise, emit error message
559 reporter
.printError("No sources files found for package " + classOrPackage
);
564 //--- Complain if no packages or classes specified
571 //--- Validate custom options passed on command line
572 // by asking the Doclet if they are OK.
574 String
[][] customOptionArr
= (String
[][]) options
575 .toArray(new String
[0][0]);
576 if (validOptionsMethod
!= null
577 && !((Boolean
) validOptionsMethod
.invoke(null, new Object
[]
578 { customOptionArr
, reporter
})).booleanValue())
580 // Not ok: shutdown system.
581 reporter
.printNotice(STRING_TRY_GJDOC_HELP
);
585 if (!rootDoc
.hasSpecifiedPackagesOrClasses()) {
586 reporter
.printError("No packages or classes specified.");
587 reporter
.printNotice(STRING_TRY_GJDOC_HELP
);
591 rootDoc
.setOptions(customOptionArr
);
595 //--- Bail out if no classes found
597 if (0 == rootDoc
.classes().length
598 && 0 == rootDoc
.specifiedPackages().length
599 && 0 == rootDoc
.specifiedClasses().length
)
601 reporter
.printError("No packages or classes found(!).");
605 //--- Our work is done, tidy up memory
610 //--- Set flag indicating Phase II of documentation generation
612 docletRunning
= true;
614 //--- Invoke the start method on the Doclet: produce output
616 reporter
.printNotice("Running doclet...");
618 TemporaryStore tstore
= new TemporaryStore(Main
.rootDoc
);
620 Thread
.currentThread().setContextClassLoader(docletClass
.getClassLoader());
622 if (null != startTempMethod
)
624 startTempMethod
.invoke(null, new Object
[]
629 startMethod
.invoke(null, new Object
[]
630 { tstore
.getAndClear() });
633 //--- Let the user know how many warnings/errors occured
635 if (reporter
.getWarningCount() > 0)
637 reporter
.printNotice(reporter
.getWarningCount() + " warnings");
640 if (reporter
.getErrorCount() > 0)
642 reporter
.printNotice(reporter
.getErrorCount() + " errors");
657 private void addFoundPackages(String subpackage
, Set foundPackages
)
659 if (foundPackages
.isEmpty()) {
660 reporter
.printWarning("No classes found under subpackage " + subpackage
);
663 boolean onePackageAdded
= false;
664 for (Iterator rit
= foundPackages
.iterator(); rit
.hasNext();) {
665 String foundPackage
= (String
)rit
.next();
666 boolean excludeThisPackage
= false;
668 for (Iterator eit
= option_exclude
.iterator(); eit
.hasNext();) {
669 String excludePackage
= (String
)eit
.next();
670 if (foundPackage
.equals(excludePackage
) ||
671 foundPackage
.startsWith(excludePackage
+ ":")) {
672 excludeThisPackage
= true;
677 if (!excludeThisPackage
) {
678 rootDoc
.addSpecifiedPackageName(foundPackage
);
679 onePackageAdded
= true;
682 if (!onePackageAdded
) {
683 if (null != subpackage
) {
684 reporter
.printWarning("No non-excluded classes found under subpackage " + subpackage
);
687 reporter
.printWarning("No non-excluded classes found.");
694 * Verify that the given file is a valid Java source file and that
695 * it specifies the given package.
697 private boolean isValidJavaFile(File file
,
698 String expectedPackage
)
701 InputStream in
= new BufferedInputStream(new FileInputStream(file
));
703 int ch
, prevChar
= 0;
705 final int STATE_DEFAULT
= 0;
706 final int STATE_COMMENT
= 1;
707 final int STATE_LINE_COMMENT
= 2;
709 int state
= STATE_DEFAULT
;
711 StringBuffer word
= new StringBuffer();
714 while ((ch
= in
.read()) >= 0) {
715 String completeWord
= null;
719 if (prevChar
== '*' && ch
== '/') {
720 state
= STATE_DEFAULT
;
724 case STATE_LINE_COMMENT
:
726 state
= STATE_DEFAULT
;
731 if (prevChar
== '/' && ch
== '*') {
732 word
.deleteCharAt(word
.length() - 1);
733 if (word
.length() > 0) {
734 completeWord
= word
.toString();
737 state
= STATE_COMMENT
;
739 else if (prevChar
== '/' && ch
== '/') {
740 word
.deleteCharAt(word
.length() - 1);
741 if (word
.length() > 0) {
742 completeWord
= word
.toString();
745 state
= STATE_LINE_COMMENT
;
747 else if (" \t\r\n".indexOf(ch
) >= 0) {
748 if (word
.length() > 0) {
749 completeWord
= word
.toString();
753 else if (1 == wordIndex
&& ';' == ch
) {
754 if (word
.length() > 0) {
755 completeWord
= word
.toString();
759 // empty package name in source file: "package ;" -> invalid source file
765 word
.append((char)ch
);
770 if (null != completeWord
) {
771 if (0 == wordIndex
&& !"package".equals(completeWord
)) {
773 return "".equals(expectedPackage
);
775 else if (1 == wordIndex
) {
777 return expectedPackage
.equals(completeWord
);
785 // no package or class found before end-of-file -> invalid source file
790 catch (IOException e
) {
791 reporter
.printWarning("Could not examine file " + file
+ ": " + e
);
797 * Recursively try to locate valid Java packages under the given
798 * package specified by its name and its directory. Add the names
799 * of all valid packages to the result list.
801 private void findPackages(String subpackage
,
805 File
[] files
= packageDir
.listFiles();
807 for (int i
=0; i
<files
.length
; ++i
) {
808 File file
= files
[i
];
809 if (!file
.isDirectory() && file
.getName().endsWith(".java")) {
810 if (isValidJavaFile(file
, subpackage
)) {
811 if ("".equals(subpackage
)) {
815 result
.add(subpackage
);
821 for (int i
=0; i
<files
.length
; ++i
) {
822 File file
= files
[i
];
823 if (file
.isDirectory()) {
824 String newSubpackage
;
825 if (null != subpackage
&& subpackage
.length() > 0) {
826 newSubpackage
= subpackage
+ "." + file
.getName();
829 newSubpackage
= file
.getName();
831 findPackages(newSubpackage
, file
, result
);
840 private static boolean validOptions(String options
[][],
841 DocErrorReporter reporter
)
844 boolean foundDocletOption
= false;
845 for (int i
= 0; i
< options
.length
; i
++)
847 String
[] opt
= options
[i
];
848 if (opt
[0].equalsIgnoreCase("-doclet"))
850 if (foundDocletOption
)
852 reporter
.printError("Only one -doclet option allowed.");
857 foundDocletOption
= true;
866 * Main entry point. This is the method called when gjdoc is invoked from the
870 * command line arguments
872 public static void main(String
[] args
)
877 //--- Remember current time for profiling purposes
879 Timer
.setStartTime();
881 //--- Handle control to the Singleton instance of this class
883 int result
= instance
.start(args
);
889 else if (result
> 0) {
890 // errors encountered
900 //--- unexpected error
907 * Parses command line arguments and subsequentially handles control to the
908 * startDoclet() method
910 * @param args The command line parameters.
912 public static int execute(String
[] args
)
916 int result
= instance
.start(args
);
921 else if (result
> 0) {
922 // errors encountered
938 * @param programName Name of the program (for error messages). *disregarded*
939 * @param args The command line parameters.
940 * @returns The return code.
942 public static int execute(String programName
,
945 return execute(args
);
949 * @param programName Name of the program (for error messages).
950 * @param defaultDocletClassName Fully qualified class name.
951 * @param args The command line parameters.
952 * @returns The return code.
954 public static int execute(String programName,
955 String defaultDocletClassName,
958 // not yet implemented
962 * @param programName Name of the program (for error messages).
963 * @param defaultDocletClassName Fully qualified class name.
964 * @param args The command line parameters.
965 * @returns The return code.
967 public static int execute(String programName,
968 String defaultDocletClassName,
971 // not yet implemented
975 * @param programName Name of the program (for error messages).
976 * @param errWriter PrintWriter to receive error messages.
977 * @param warnWriter PrintWriter to receive error messages.
978 * @param noticeWriter PrintWriter to receive error messages.
979 * @param defaultDocletClassName Fully qualified class name.
980 * @param args The command line parameters.
981 * @returns The return code.
983 public static int execute(String programName,
984 PrintWriter errWriter,
985 PrintWriter warnWriter,
986 PrintWriter noticeWriter,
987 String defaultDocletClassName,
990 // not yet implemented
994 * Parses command line arguments and subsequentially handles control to the
995 * startDoclet() method
998 * Command line arguments, as passed to the main() method
999 * @return {@code -1} in case of a fatal error (invalid arguments),
1000 * or the number of errors encountered.
1001 * @exception ParseException
1003 * @exception IOException
1004 * if an IO problem occur
1006 public int start(String
[] args
) throws ParseException
, IOException
1009 //--- Collect unparsed arguments in array and resolve references
1010 // to external argument files.
1012 List arguments
= new ArrayList(args
.length
);
1014 for (int i
= 0; i
< args
.length
; ++i
)
1016 if (!args
[i
].startsWith("@"))
1018 arguments
.add(args
[i
]);
1022 FileReader reader
= new FileReader(args
[i
].substring(1));
1023 StreamTokenizer st
= new StreamTokenizer(reader
);
1025 st
.wordChars('\u0000', '\uffff');
1028 st
.whitespaceChars(' ', ' ');
1029 st
.whitespaceChars('\t', '\t');
1030 st
.whitespaceChars('\r', '\r');
1031 st
.whitespaceChars('\n', '\n');
1032 while (st
.nextToken() != StreamTokenizer
.TT_EOF
)
1034 arguments
.add(st
.sval
);
1039 //--- Initialize Map for option parsing
1043 //--- This will hold all options recognized by gjdoc itself
1044 // and their associated arguments.
1045 // Contains objects of type String[], where each entry
1046 // specifies an option along with its aguments.
1048 List options
= new LinkedList();
1050 //--- This will hold all command line tokens not recognized
1051 // to be part of a standard option.
1052 // These options are intended to be processed by the doclet
1053 // Contains objects of type String, where each entry is
1054 // one unrecognized token.
1056 List customOptions
= new LinkedList();
1058 rootDoc
= new RootDocImpl();
1059 reporter
= rootDoc
.getReporter();
1061 //--- Iterate over all options given on the command line
1063 for (Iterator it
= arguments
.iterator(); it
.hasNext();)
1066 String arg
= (String
) it
.next();
1068 //--- Check if gjdoc recognizes this option as a standard option
1069 // and remember the options' argument count
1071 int optlen
= optionLength(arg
);
1073 //--- Argument count == 0 indicates that the option is not recognized.
1074 // Add it to the list of custom option tokens
1076 //--- Otherwise the option is recognized as a standard option.
1077 // if all required arguments are supplied. Create a new String
1078 // array for the option and its arguments, and store it
1079 // in the options array.
1083 String
[] option
= new String
[optlen
];
1085 boolean optargs_ok
= true;
1086 for (int j
= 1; j
< optlen
&& optargs_ok
; ++j
)
1090 option
[j
] = (String
) it
.next();
1091 if (option
[j
].startsWith("-"))
1102 options
.add(option
);
1105 // If the option requires more arguments than given on the
1106 // command line, issue a fatal error
1108 reporter
.printFatal("Missing value for option " + arg
+ ".");
1113 //--- Create an array of String arrays from the dynamic array built above
1115 String
[][] optionArr
= (String
[][]) options
.toArray(new String
[options
1118 //--- Validate all options and issue warnings/errors
1120 if (validOptions(optionArr
, rootDoc
))
1123 //--- We got valid options; parse them and store the parsed values
1124 // in 'option_*' fields.
1126 readOptions(optionArr
);
1128 //--- Show version and exit if requested by user
1130 if (option_showVersion
) {
1131 System
.out
.println("gjdoc " + getGjdocVersion());
1135 if (option_bootclasspath_specified
) {
1136 reporter
.printWarning("-bootclasspath ignored: not supported by"
1137 + " gjdoc wrapper script, or no wrapper script in use.");
1140 // If we have an empty source path list, add the current directory ('.')
1142 if (option_sourcepath
.size() == 0)
1143 option_sourcepath
.add(new File("."));
1145 //--- We have all information we need to start the doclet at this time
1147 if (null != option_encoding
) {
1148 rootDoc
.setSourceEncoding(option_encoding
);
1151 // be quiet about this for now:
1152 // reporter.printNotice("No encoding specified, using platform default: " + System.getProperty("file.encoding"));
1153 rootDoc
.setSourceEncoding(System
.getProperty("file.encoding"));
1155 rootDoc
.setSourcePath(option_sourcepath
);
1157 //addJavaLangClasses();
1159 if (!startDoclet(arguments
)) {
1164 return reporter
.getErrorCount();
1167 private void addJavaLangClasses()
1170 String resourceName
= "/java.lang-classes-" + option_source
+ ".txt";
1171 InputStream in
= getClass().getResourceAsStream(resourceName
);
1172 BufferedReader reader
= new BufferedReader(new InputStreamReader(in
));
1174 while ((line
= reader
.readLine()) != null) {
1176 String className
= line
.trim();
1177 if (className
.length() > 0) {
1178 ClassDocImpl classDoc
=
1179 new ClassDocImpl(null, new PackageDocImpl("java.lang"),
1180 ProgramElementDocImpl
.ACCESS_PUBLIC
,
1181 false, false, null);
1182 classDoc
.setClass(className
);
1183 rootDoc
.addClassDoc(classDoc
);
1189 * Helper class for parsing command line arguments. An instance of this class
1190 * represents a particular option accepted by gjdoc (e.g. '-sourcepath') along
1191 * with the number of expected arguments and behavior to parse the arguments.
1193 private abstract class OptionProcessor
1197 * Number of arguments expected by this option.
1199 private int argCount
;
1202 * Initializes this instance.
1205 * number of arguments
1207 public OptionProcessor(int argCount
)
1209 this.argCount
= argCount
;
1213 * Overridden by derived classes with behavior to parse the arguments
1214 * specified with this option.
1217 * command line arguments
1219 abstract void process(String
[] args
);
1223 * Maps option tags (e.g. '-sourcepath') to OptionProcessor objects.
1224 * Initialized only once by method initOptions(). FIXME: Rename to
1225 * 'optionProcessors'.
1227 private static Map options
= null;
1230 * Initialize all OptionProcessor objects needed to scan/parse command line
1231 * options. This cannot be done in a static initializer block because
1232 * OptionProcessors need access to the Singleton instance of the Main class.
1234 private void initOptions()
1237 options
= new HashMap();
1239 //--- Put one OptionProcessor object into the map
1240 // for each option recognized.
1242 options
.put("-overview", new OptionProcessor(2)
1245 void process(String
[] args
)
1247 option_overview
= args
[0];
1250 options
.put("-public", new OptionProcessor(1)
1253 void process(String
[] args
)
1255 option_coverage
= COVERAGE_PUBLIC
;
1258 options
.put("-protected", new OptionProcessor(1)
1261 void process(String
[] args
)
1263 option_coverage
= COVERAGE_PROTECTED
;
1266 options
.put("-package", new OptionProcessor(1)
1269 void process(String
[] args
)
1271 option_coverage
= COVERAGE_PACKAGE
;
1274 options
.put("-private", new OptionProcessor(1)
1277 void process(String
[] args
)
1279 option_coverage
= COVERAGE_PRIVATE
;
1282 OptionProcessor helpProcessor
= new OptionProcessor(1)
1285 void process(String
[] args
)
1291 options
.put("-help", helpProcessor
);
1292 options
.put("--help", helpProcessor
);
1293 options
.put("-doclet", new OptionProcessor(2)
1296 void process(String
[] args
)
1298 option_doclet
= args
[0];
1301 options
.put("-docletpath", new OptionProcessor(2)
1304 void process(String
[] args
)
1306 option_docletpath
= args
[0];
1309 options
.put("-nowarn", new OptionProcessor(1)
1312 void process(String
[] args
)
1314 option_nowarn
= true;
1317 options
.put("-source", new OptionProcessor(2)
1320 void process(String
[] args
)
1322 option_source
= args
[0];
1323 if (!"1.2".equals(option_source
)
1324 && !"1.3".equals(option_source
)
1325 && !"1.4".equals(option_source
)) {
1327 throw new RuntimeException("Only he following values are currently"
1328 + " supported for option -source: 1.2, 1.3, 1.4.");
1332 OptionProcessor sourcePathProcessor
= new OptionProcessor(2) {
1333 void process(String
[] args
)
1335 Debug
.log(1, "-sourcepath is '" + args
[0] + "'");
1336 for (StringTokenizer st
= new StringTokenizer(args
[0],
1337 File
.pathSeparator
); st
.hasMoreTokens();)
1339 String path
= st
.nextToken();
1340 File file
= new File(path
);
1341 if (!(file
.exists()))
1343 throw new RuntimeException("The source path " + path
1344 + " does not exist.");
1346 option_sourcepath
.add(file
);
1350 options
.put("-s", sourcePathProcessor
);
1351 options
.put("-sourcepath", sourcePathProcessor
);
1352 options
.put("-subpackages", new OptionProcessor(2)
1354 void process(String
[] args
)
1356 StringTokenizer st
= new StringTokenizer(args
[0], ":");
1357 while (st
.hasMoreTokens()) {
1358 String packageName
= st
.nextToken();
1360 if (packageName
.startsWith(".")
1361 || packageName
.endsWith(".")
1362 || packageName
.indexOf("..") > 0
1363 || !checkCharSet(packageName
,
1364 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_.")) {
1365 throw new RuntimeException("Illegal package name '"
1366 + packageName
+ "'");
1368 option_subpackages
.add(packageName
);
1372 options
.put("-exclude", new OptionProcessor(2)
1374 void process(String
[] args
)
1376 StringTokenizer st
= new StringTokenizer(args
[0], ":");
1377 while (st
.hasMoreTokens()) {
1378 String packageName
= st
.nextToken();
1380 if (packageName
.startsWith(".")
1381 || packageName
.endsWith(".")
1382 || packageName
.indexOf("..") > 0
1383 || !checkCharSet(packageName
,
1384 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_.")) {
1385 throw new RuntimeException("Illegal package name '"
1386 + packageName
+ "'");
1388 option_exclude
.add(packageName
);
1392 // TODO include other options here
1393 options
.put("-verbose", new OptionProcessor(1)
1396 void process(String
[] args
)
1398 option_verbose
= true;
1399 System
.err
.println("WARNING: Unsupported option -verbose ignored");
1402 options
.put("-quiet", new OptionProcessor(1)
1405 void process(String
[] args
)
1407 reporter
.setQuiet(true);
1410 options
.put("-locale", new OptionProcessor(2)
1413 void process(String
[] args
)
1415 String localeName
= args
[0];
1416 String language
= null;
1417 String country
= null;
1418 String variant
= null;
1419 StringTokenizer st
= new StringTokenizer(localeName
, "_");
1420 if (st
.hasMoreTokens()) {
1421 language
= st
.nextToken();
1423 if (st
.hasMoreTokens()) {
1424 country
= st
.nextToken();
1426 if (st
.hasMoreTokens()) {
1427 variant
= st
.nextToken();
1429 if (variant
!= null) {
1430 option_locale
= new Locale(language
, country
, variant
);
1432 else if (country
!= null) {
1433 option_locale
= new Locale(language
, country
);
1435 else if (language
!= null) {
1436 option_locale
= new Locale(language
);
1439 throw new RuntimeException("Illegal locale specification '"
1440 + localeName
+ "'");
1444 options
.put("-encoding", new OptionProcessor(2)
1447 void process(String
[] args
)
1449 option_encoding
= args
[0];
1452 options
.put("-breakiterator", new OptionProcessor(1)
1454 void process(String
[] args
)
1456 option_breakiterator
= true;
1459 options
.put("-licensetext", new OptionProcessor(1)
1461 void process(String
[] args
)
1463 option_licensetext
= true;
1466 options
.put("-overview", new OptionProcessor(2)
1468 void process(String
[] args
)
1471 getRootDoc().setRawCommentText(RootDocImpl
.readHtmlBody(new File(args
[0])));
1473 catch (IOException e
) {
1474 throw new RuntimeException("Cannot read file specified in option -overview: " + e
.getMessage());
1478 options
.put("-classpath", new OptionProcessor(2)
1480 void process(String
[] args
)
1482 reporter
.printWarning("-classpath option could not be passed to the VM. Faking it with ");
1483 reporter
.printWarning(" System.setProperty(\"java.class.path\", \"" + args
[0] + "\");");
1484 System
.setProperty("java.class.path", args
[0]);
1487 options
.put("--version", new OptionProcessor(1)
1489 void process(String
[] args
)
1491 option_showVersion
= true;
1494 options
.put("-bootclasspath", new OptionProcessor(1)
1496 void process(String
[] args
)
1498 option_bootclasspath_specified
= true;
1501 options
.put("-all", new OptionProcessor(1)
1503 void process(String
[] args
)
1508 options
.put("-reflection", new OptionProcessor(1)
1510 void process(String
[] args
)
1512 option_reflection
= true;
1518 * Determine how many arguments the given option requires.
1521 * The name of the option without leading dash.
1523 private static int optionLength(String option
)
1526 OptionProcessor op
= (OptionProcessor
) options
.get(option
.toLowerCase());
1534 * Process all given options. Assumes that the options have been validated
1538 * Each element is a series of Strings where [0] is the name of the
1539 * option and [1..n] are the arguments to the option.
1541 private void readOptions(String
[][] optionArr
)
1544 //--- For each option, find the appropriate OptionProcessor
1545 // and call its process() method
1547 for (int i
= 0; i
< optionArr
.length
; ++i
)
1549 String
[] opt
= optionArr
[i
];
1550 String
[] args
= new String
[opt
.length
- 1];
1551 System
.arraycopy(opt
, 1, args
, 0, opt
.length
- 1);
1552 OptionProcessor op
= (OptionProcessor
) options
.get(opt
[0].toLowerCase());
1558 * Print command line usage.
1560 private static void usage()
1564 + "USAGE: gjdoc [options] [packagenames] "
1565 + "[sourcefiles] [@files]\n\n"
1566 + " --version Show version information and exit\n"
1567 + " -all Process all source files found in the source path\n"
1568 + " -overview <file> Read overview documentation from HTML file\n"
1569 + " -public Include only public classes and members\n"
1570 + " -protected Include protected and public classes and members\n"
1571 + " This is the default\n"
1572 + " -package Include package/protected/public classes and members\n"
1573 + " -private Include all classes and members\n"
1574 + " -help, --help Show this information\n"
1575 + " -doclet <class> Doclet class to use for generating output\n"
1576 + " -docletpath <classpath> Specifies the search path for the doclet and\n"
1578 + " -source <release> Provide source compatibility with specified\n"
1579 + " release (1.4 to handle assertion)\n"
1580 + " -sourcepath <pathlist> Where to look for source files\n"
1581 + " -s <pathlist> Alias for -sourcepath\n"
1582 + " -subpackages <spkglist> List of subpackages to recursively load\n"
1583 + " -exclude <pkglist> List of packages to exclude\n"
1584 + " -verbose Output messages about what Gjdoc is doing [ignored]\n"
1585 + " -quiet Do not print non-error and non-warning messages\n"
1586 + " -locale <name> Locale to be used, e.g. en_US or en_US_WIN\n"
1587 + " -encoding <name> Source file encoding name\n"
1588 + " -breakiterator Compute first sentence with BreakIterator\n"
1589 + " -classpath <pathlist> Set the path used for loading auxilliary classes\n"
1591 + "Standard doclet options:\n"
1592 + " -d Set target directory\n"
1593 + " -use Includes the 'Use' page for each documented class\n"
1595 + " -version Includes the '@version' tag\n"
1596 + " -author Includes the '@author' tag\n"
1597 + " -splitindex Splits the index file into multiple files\n"
1598 + " -windowtitle <text> Browser window title\n"
1599 + " -doctitle <text> Title near the top of the overview summary file\n"
1600 + " (HTML allowed)\n"
1601 + " -title <text> Title for this set of API documentation\n"
1602 + " (deprecated, -doctitle should be used instead)\n"
1603 + " -header <text> Text to include in the top navigation bar\n"
1604 + " (HTML allowed)\n"
1605 + " -footer <text> Text to include in the bottom navigation bar\n"
1606 + " (HTML allowed)\n"
1607 + " -bottom <text> Text to include at the bottom of each output file\n"
1608 + " (HTML allowed)\n"
1609 + " -link <extdoc URL> Link to external generated documentation at URL\n"
1610 + " -linkoffline <extdoc URL> <packagelistLoc>\n"
1611 + " Link to external generated documentation for\n"
1612 + " the specified package-list\n"
1613 + " -linksource Creates an HTML version of each source file\n"
1614 + " -group <groupheading> <packagepattern:packagepattern:...>\n"
1615 + " Separates packages on the overview page into groups\n"
1616 + " -nodeprecated Prevents the generation of any deprecated API\n"
1617 + " -nodeprecatedlist Prevents the generation of the file containing\n"
1618 + " the list of deprecated APIs and the link to the\n"
1619 + " navigation bar to that page\n"
1620 + " -nosince Omit the '@since' tag\n"
1621 + " -notree Do not generate the class/interface hierarchy page\n"
1622 + " -noindex Do not generate the index file\n"
1623 + " -nohelp Do not generate the help link\n"
1624 + " -nonavbar Do not generate the navbar, header and footer\n"
1625 + " -helpfile <filen> Path to an alternate help file\n"
1626 + " -stylesheetfile <file> Path to an alternate CSS stylesheet\n"
1627 + " -addstylesheet <file> Path to an additional CSS stylesheet\n"
1628 + " -serialwarn Complain about missing '@serial' tags [ignored]\n"
1629 + " -charset <IANACharset> Specifies the HTML charset\n"
1630 + " -docencoding <IANACharset>\n"
1631 + " Specifies the encoding of the generated HTML files\n"
1632 + " -tag <tagname>:Xaoptcmf:\"<taghead>\"\n"
1633 + " Enables gjdoc to interpret a custom tag\n"
1634 + " -taglet Adds a Taglet class to the map of taglets\n"
1635 + " -tagletpath Sets the CLASSPATH to load subsequent Taglets from\n"
1636 + " -docfilessubdirs Enables deep copy of 'doc-files' directories\n"
1637 + " -excludedocfilessubdir <name1:name2:...>\n"
1638 + " Excludes 'doc-files' subdirectories with a give name\n"
1639 + " -noqualifier all|<packagename1:packagename2:...>\n"
1640 + " Do never fully qualify given package names\n"
1641 + " -nocomment Suppress the entire comment body including the main\n"
1642 + " description and all tags, only generate declarations\n"
1644 + "Gjdoc extension options:\n"
1645 + " -reflection Use reflection for resolving unqualified class names\n"
1646 + " -licensetext Include license text from source files\n"
1647 + " -validhtml Use valid HTML/XML names (breaks compatibility)\n"
1648 + " -baseurl <url> Hardwire the given base URL into generated pages\n"
1650 + " -genhtml Generate HTML code instead of XML code. This is the\n"
1652 + " -geninfo Generate Info code instead of XML code.\n"
1653 + " -xslsheet <file> If specified, XML files will be written to a\n"
1654 + " temporary directory and transformed using the\n"
1655 + " given XSL sheet. The result of the transformation\n"
1656 + " is written to the output directory. Not required if\n"
1657 + " -genhtml or -geninfo has been specified.\n"
1658 + " -xmlonly Generate XML code only, do not generate HTML code.\n"
1659 + " -bottomnote HTML code to include at the bottom of each page.\n"
1660 + " -nofixhtml If not specified, heurestics will be applied to\n"
1661 + " fix broken HTML code in comments.\n"
1662 + " -nohtmlwarn Do not emit warnings when encountering broken HTML\n"
1664 + " -noemailwarn Do not emit warnings when encountering strings like\n"
1665 + " <abc@foo.com>.\n"
1666 + " -indentstep <n> How many spaces to indent each tag level in\n"
1667 + " generated XML code.\n"
1668 + " -xsltdriver <class> Specifies the XSLT driver to use for transformation.\n"
1669 + " By default, xsltproc is used.\n"
1670 + " -postprocess <class> XmlDoclet postprocessor class to apply after XSL\n"
1671 + " transformation.\n"
1672 + " -compress Generated info pages will be Zip-compressed.\n"
1673 + " -workpath Specify a temporary directory to use.\n"
1674 + " -authormail <type> Specify handling of mail addresses in @author tags.\n"
1675 + " no-replace do not replace mail addresses (default).\n"
1676 + " mailto-name replace by <a>Real Name</a>.\n"
1677 + " name-mailto-address replace by Real Name (<a>abc@foo.com</a>).\n"
1678 + " name-mangled-address replace by Real Name (<a>abc AT foo DOT com</a>).\n"
1684 * The root of the gjdoc tool.
1686 * @return all the options of the gjdoc application.
1688 public static RootDocImpl
getRootDoc()
1694 * Get the gjdoc singleton.
1696 * @return the gjdoc instance.
1698 public static Main
getInstance()
1704 * Is this access level covered?
1706 * @param accessLevel
1707 * the access level we want to know if it is covered.
1708 * @return true if the access level is covered.
1710 public boolean includeAccessLevel(int accessLevel
)
1712 return coverageTemplates
[option_coverage
][accessLevel
];
1716 * Is the doclet running?
1718 * @return true if it's running
1720 public boolean isDocletRunning()
1722 return docletRunning
;
1726 * Check the charset. Check that all the characters of the string 'toCheck'
1727 * and query if they exist in the 'charSet'. The order does not matter. The
1728 * number of times a character is in the variable does not matter.
1731 * the charset to check.
1733 * the reference charset
1734 * @return true if they match.
1736 public static boolean checkCharSet(String toCheck
, String charSet
)
1738 for (int i
= 0; i
< toCheck
.length(); ++i
)
1740 if (charSet
.indexOf(toCheck
.charAt(i
)) < 0)
1747 * Makes the RootDoc eligible for the GC.
1749 public static void releaseRootDoc()
1755 * Return whether the -breakiterator option has been specified.
1757 public boolean isUseBreakIterator()
1759 return this.option_breakiterator
1760 || !getLocale().getLanguage().equals(Locale
.ENGLISH
.getLanguage());
1764 * Return whether boilerplate license text should be copied.
1766 public boolean isCopyLicenseText()
1768 return this.option_licensetext
;
1772 * Return the locale specified using the -locale option or the
1775 public Locale
getLocale()
1777 return this.option_locale
;
1781 * Return the collator to use based on the specified -locale
1782 * option. If no collator can be found for the given locale, a
1783 * warning is emitted and the default collator is used instead.
1785 public Collator
getCollator()
1787 if (null == this.collator
) {
1788 Locale locale
= getLocale();
1789 this.collator
= Collator
.getInstance(locale
);
1790 Locale defaultLocale
= Locale
.getDefault();
1791 if (null == this.collator
1792 && !defaultLocale
.equals(locale
)) {
1793 this.collator
= Collator
.getInstance(defaultLocale
);
1794 if (null != this.collator
) {
1795 reporter
.printWarning("No collator found for locale "
1796 + locale
.getDisplayName()
1797 + "; using collator for default locale "
1798 + defaultLocale
.getDisplayName()
1802 this.collator
= Collator
.getInstance();
1803 reporter
.printWarning("No collator found for specified locale "
1804 + locale
.getDisplayName()
1805 + " or default locale "
1806 + defaultLocale
.getDisplayName()
1807 + ": using default collator.");
1810 if (null == this.collator
) {
1811 this.collator
= Collator
.getInstance();
1812 reporter
.printWarning("No collator found for locale "
1813 + locale
.getDisplayName()
1814 + ": using default collator.");
1817 return this.collator
;
1820 public boolean isCacheRawComments()
1825 public String
getGjdocVersion()
1827 if (null == gjdocVersion
) {
1828 gjdocVersion
= gnu
.classpath
.Configuration
.CLASSPATH_VERSION
;
1830 return gjdocVersion
;
1833 public boolean isReflectionEnabled()
1835 return this.option_reflection
;