2009-07-17 Richard Guenther <rguenther@suse.de>
[official-gcc.git] / libjava / classpath / tools / gnu / classpath / tools / gjdoc / Main.java
blobcbbc8f4f7ddf997e2ec49f05aae9473fb6fccee1
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)
9 any later version.
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
19 02111-1307 USA. */
21 package gnu.classpath.tools.gjdoc;
23 import com.sun.javadoc.*;
24 import java.io.*;
25 import java.util.*;
26 import java.lang.reflect.*;
27 import java.text.Collator;
29 import gnu.classpath.tools.FileSystemClassLoader;
31 /**
32 * Class that will launch the gjdoc tool.
34 public final class Main
37 /**
38 * Do we load classes that are referenced as base class?
40 static final boolean DESCEND_SUPERCLASS = true;
42 /**
43 * Do we load classes that are referenced as interface?
45 static final boolean DESCEND_INTERFACES = false;
47 /**
48 * Do we load classes that are imported in a source file?
50 static final boolean DESCEND_IMPORTED = true;
52 /**
53 * Document only public members.
55 static final int COVERAGE_PUBLIC = 0;
57 /**
58 * Document only public and protected members.
60 static final int COVERAGE_PROTECTED = 1;
62 /**
63 * Document public, protected and package private members.
65 static final int COVERAGE_PACKAGE = 2;
67 /**
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.";
78 /**
79 * Grid for looking up whether a particular access level is included in the
80 * documentation.
82 static final boolean[][] coverageTemplates = new boolean[][]
83 { new boolean[]
84 { true, false, false, false }, // public
85 new boolean[]
86 { true, true, false, false }, // protected
87 new boolean[]
88 { true, true, true, false }, // package
89 new boolean[]
90 { true, true, true, true }, // private
93 /**
94 * Holds the Singleton instance of this class.
96 private static Main instance = new Main();
98 /**
99 * Avoid re-instantiation of this class.
101 private Main()
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
190 * longer supported.
192 private String option_source = "1.2";
195 * Option "-subpackages": list of subpackages to be recursively
196 * added.
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...");
259 Class docletClass;
261 if (null != option_docletpath) {
262 try {
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);
272 else {
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[]
289 { String.class });
291 catch (NoSuchMethodException e)
293 // Ignore if not found; it's OK it the Doclet class doesn't define
294 // this method.
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
307 // this method.
310 //--- Find the start method in the Doclet class; complain if not found
314 startTempMethod = docletClass.getMethod("start", new Class[]
315 { TemporaryStore.class });
317 catch (Exception e)
319 // ignore
321 startMethod = docletClass.getMethod("start", new Class[]
322 { RootDoc.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("-"))
341 //--- Parse option
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[]
351 { option });
353 Debug.log(3, "invoking optionLen method");
355 optlen = ((Integer) optionLenMethod.invoke(null, new Object[]
356 { option })).intValue();
358 Debug.log(3, "done");
361 if (optlen <= 0) {
363 if (option.startsWith("-JD")) {
364 // Simulate VM option -D
365 String propertyValue = option.substring(3);
366 int ndx = propertyValue.indexOf('=');
367 if (ndx <= 0) {
368 reporter.printError("Illegal format in option " + option + ": use -JDproperty=value");
369 return false;
371 else {
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.");
381 else {
382 //--- Complain if not found
384 reporter.printError("Unknown option " + option);
385 reporter.printNotice(STRING_TRY_GJDOC_HELP);
386 return false;
389 else
392 //--- Read option values
394 String[] optionAndValues = new String[optlen];
395 optionAndValues[0] = option;
396 for (int i = 1; i < optlen; ++i)
398 if (!it.hasNext())
400 reporter.printError("Missing value for option " + option);
401 return false;
403 else
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
418 // value
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
428 // beneath it.
430 //--- For each class or package specified on the command line,
431 // check that it exists and find out whether it is a class
432 // or a package
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);
448 if (option_all) {
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) {
458 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;
478 break;
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('.',
506 File.separatorChar);
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
522 + " not found.");
523 return false;
526 //--- Complain if both exist: ambigious
528 else
529 if (packageDirExists && sourceFileExists)
531 reporter.printError("Ambigious class/package name "
532 + classOrPackage + ".");
533 return false;
536 //--- Otherwise, if the package directory exists, it is a package
538 else
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;
547 break;
550 if (!packageDirFound) {
551 reporter.printError("No suitable file or directory found for" + classOrPackage);
552 return false;
556 //--- Otherwise, emit error message
558 else {
559 reporter.printError("No sources files found for package " + classOrPackage);
564 //--- Complain if no packages or classes specified
566 if (option_help) {
567 usage();
568 return true;
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);
582 return false;
585 if (!rootDoc.hasSpecifiedPackagesOrClasses()) {
586 reporter.printError("No packages or classes specified.");
587 reporter.printNotice(STRING_TRY_GJDOC_HELP);
588 return false;
591 rootDoc.setOptions(customOptionArr);
593 rootDoc.build();
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(!).");
602 return false;
605 //--- Our work is done, tidy up memory
607 System.gc();
608 System.gc();
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[]
625 { tstore });
627 else
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");
645 System.gc();
647 //--- Done.
648 return true;
650 catch (Exception e)
652 e.printStackTrace();
653 return false;
657 private void addFoundPackages(String subpackage, Set foundPackages)
659 if (foundPackages.isEmpty()) {
660 reporter.printWarning("No classes found under subpackage " + subpackage);
662 else {
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;
673 break;
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);
686 else {
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)
700 try {
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();
712 int wordIndex = 0;
714 while ((ch = in.read()) >= 0) {
715 String completeWord = null;
717 switch (state) {
718 case STATE_COMMENT:
719 if (prevChar == '*' && ch == '/') {
720 state = STATE_DEFAULT;
722 break;
724 case STATE_LINE_COMMENT:
725 if (ch == '\n') {
726 state = STATE_DEFAULT;
728 break;
730 case STATE_DEFAULT:
731 if (prevChar == '/' && ch == '*') {
732 word.deleteCharAt(word.length() - 1);
733 if (word.length() > 0) {
734 completeWord = word.toString();
735 word.setLength(0);
737 state = STATE_COMMENT;
739 else if (prevChar == '/' && ch == '/') {
740 word.deleteCharAt(word.length() - 1);
741 if (word.length() > 0) {
742 completeWord = word.toString();
743 word.setLength(0);
745 state = STATE_LINE_COMMENT;
747 else if (" \t\r\n".indexOf(ch) >= 0) {
748 if (word.length() > 0) {
749 completeWord = word.toString();
750 word.setLength(0);
753 else if (1 == wordIndex && ';' == ch) {
754 if (word.length() > 0) {
755 completeWord = word.toString();
756 word.setLength(0);
758 else {
759 // empty package name in source file: "package ;" -> invalid source file
760 in.close();
761 return false;
764 else {
765 word.append((char)ch);
767 break;
770 if (null != completeWord) {
771 if (0 == wordIndex && !"package".equals(completeWord)) {
772 in.close();
773 return "".equals(expectedPackage);
775 else if (1 == wordIndex) {
776 in.close();
777 return expectedPackage.equals(completeWord);
779 ++ wordIndex;
782 prevChar = ch;
785 // no package or class found before end-of-file -> invalid source file
787 in.close();
788 return false;
790 catch (IOException e) {
791 reporter.printWarning("Could not examine file " + file + ": " + e);
792 return false;
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,
802 File packageDir,
803 Set result)
805 File[] files = packageDir.listFiles();
806 if (null != files) {
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)) {
812 result.add(null);
814 else {
815 result.add(subpackage);
817 break;
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();
828 else {
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.");
853 return false;
855 else
857 foundDocletOption = true;
862 return true;
866 * Main entry point. This is the method called when gjdoc is invoked from the
867 * command line.
869 * @param args
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);
885 if (result < 0) {
886 // fatal error
887 System.exit(5);
889 else if (result > 0) {
890 // errors encountered
891 System.exit(1);
893 else {
894 // success
895 System.exit(0);
898 catch (Exception e)
900 //--- unexpected error
901 e.printStackTrace();
902 System.exit(1);
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);
917 if (result < 0) {
918 // fatal error
919 return 5;
921 else if (result > 0) {
922 // errors encountered
923 return 1;
925 else {
926 // success
927 return 0;
930 catch (Exception e)
932 // unexpected error
933 return 1;
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,
943 String[] args)
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.
953 *//*
954 public static int execute(String programName,
955 String defaultDocletClassName,
956 String[] args)
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.
966 *//*
967 public static int execute(String programName,
968 String defaultDocletClassName,
969 String[] args)
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.
982 *//*
983 public static int execute(String programName,
984 PrintWriter errWriter,
985 PrintWriter warnWriter,
986 PrintWriter noticeWriter,
987 String defaultDocletClassName,
988 String[] args)
990 // not yet implemented
994 * Parses command line arguments and subsequentially handles control to the
995 * startDoclet() method
997 * @param args
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
1002 * FIXME
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]);
1020 else
1022 FileReader reader = new FileReader(args[i].substring(1));
1023 StreamTokenizer st = new StreamTokenizer(reader);
1024 st.resetSyntax();
1025 st.wordChars('\u0000', '\uffff');
1026 st.quoteChar('\"');
1027 st.quoteChar('\'');
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
1041 initOptions();
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.
1081 if (optlen > 0)
1083 String[] option = new String[optlen];
1084 option[0] = arg;
1085 boolean optargs_ok = true;
1086 for (int j = 1; j < optlen && optargs_ok; ++j)
1088 if (it.hasNext())
1090 option[j] = (String) it.next();
1091 if (option[j].startsWith("-"))
1093 optargs_ok = false;
1096 else
1098 optargs_ok = false;
1101 if (optargs_ok)
1102 options.add(option);
1103 else
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
1116 .size()][0]);
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());
1132 System.exit(0);
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);
1150 else {
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)) {
1160 return -1;
1164 return reporter.getErrorCount();
1167 private void addJavaLangClasses()
1168 throws IOException
1170 String resourceName = "/java.lang-classes-" + option_source + ".txt";
1171 InputStream in = getClass().getResourceAsStream(resourceName);
1172 BufferedReader reader = new BufferedReader(new InputStreamReader(in));
1173 String line;
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.
1204 * @param argCount
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.
1216 * @param args
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)
1287 option_help = true;
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);
1438 else {
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)
1470 try {
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)
1505 option_all = true;
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.
1520 * @param option
1521 * The name of the option without leading dash.
1523 private static int optionLength(String option)
1526 OptionProcessor op = (OptionProcessor) options.get(option.toLowerCase());
1527 if (op != null)
1528 return op.argCount;
1529 else
1530 return 0;
1534 * Process all given options. Assumes that the options have been validated
1535 * before.
1537 * @param optionArr
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());
1553 op.process(args);
1558 * Print command line usage.
1560 private static void usage()
1562 System.out
1563 .print("\n"
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"
1577 + " dependencies\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"
1590 + "\n"
1591 + "Standard doclet options:\n"
1592 + " -d Set target directory\n"
1593 + " -use Includes the 'Use' page for each documented class\n"
1594 + " and package\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"
1643 + "\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"
1651 + " default.\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"
1663 + " code.\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()
1690 return rootDoc;
1694 * Get the gjdoc singleton.
1696 * @return the gjdoc instance.
1698 public static Main getInstance()
1700 return instance;
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.
1730 * @param toCheck
1731 * the charset to check.
1732 * @param charSet
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)
1741 return false;
1743 return true;
1747 * Makes the RootDoc eligible for the GC.
1749 public static void releaseRootDoc()
1751 rootDoc.flush();
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
1773 * default locale;
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()
1799 + ".");
1801 else {
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()
1822 return true;
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;