OSDN Git Service

d1316b3418318119d4c6b9eb49e54d348626753d
[pf3gnuchains/gcc-fork.git] / libjava / classpath / tools / gnu / classpath / tools / gjdoc / Main.java
1 /* gnu.classpath.tools.gjdoc.Main
2    Copyright (C) 2001 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
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.
10  
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.
15
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. */
20
21 package gnu.classpath.tools.gjdoc;
22
23 import com.sun.javadoc.*;
24 import java.io.*;
25 import java.util.*;
26 import java.lang.reflect.*;
27 import java.text.Collator;
28
29 import gnu.classpath.tools.FileSystemClassLoader;
30
31 /**
32  * Class that will launch the gjdoc tool.
33  */
34 public final class Main
35 {
36
37   /**
38    * Do we load classes that are referenced as base class?
39    */
40   static final boolean DESCEND_SUPERCLASS = true;
41
42   /**
43    * Do we load classes that are referenced as interface?
44    */
45   static final boolean DESCEND_INTERFACES = false;
46
47   /**
48    * Do we load classes that are imported in a source file?
49    */
50   static final boolean DESCEND_IMPORTED = true;
51
52   /**
53    * Document only public members.
54    */
55   static final int COVERAGE_PUBLIC = 0;
56
57   /**
58    * Document only public and protected members.
59    */
60   static final int COVERAGE_PROTECTED = 1;
61
62   /**
63    * Document public, protected and package private members.
64    */
65   static final int COVERAGE_PACKAGE = 2;
66
67   /**
68    * Document all members.
69    */
70   static final int COVERAGE_PRIVATE = 3;
71
72   /*
73    *  FIXME: This should come from a ResourceBundle
74    */
75   private static final String STRING_TRY_GJDOC_HELP = 
76      "Try `gjdoc --help' for more information.";
77
78   /**
79    * Grid for looking up whether a particular access level is included in the
80    * documentation.
81    */
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
91     };
92
93   /**
94    * Holds the Singleton instance of this class.
95    */
96   private static Main instance = new Main();
97
98   /**
99    * Avoid re-instantiation of this class.
100    */
101   private Main()
102   {
103   }
104
105   private static RootDocImpl rootDoc;
106
107   private ErrorReporter reporter;
108
109   /**
110    * Cache for version string from resource /version.properties
111    */
112   private String gjdocVersion;
113
114   /**
115    * <code>false</code> during Phase I: preparation of the documentation data.
116    * <code>true</code> during Phase II: documentation output by doclet.
117    */
118   boolean docletRunning = false;
119
120   //---- Command line options
121
122   /**
123    * Option "-doclet": name of the Doclet class to use.
124    */
125   private String option_doclet = "gnu.classpath.tools.doclets.htmldoclet.HtmlDoclet";
126
127   /**
128    * Option "-overview": path to the special overview file.
129    */
130   private String option_overview;
131
132   /**
133    * Option "-coverage": which members to include in generated documentation.
134    */
135   private int option_coverage = COVERAGE_PROTECTED;
136
137   /**
138    * Option "-help": display command line usage.
139    */
140   private boolean option_help;
141
142   /**
143    * Option "-docletpath": path to doclet classes.
144    */
145   private String option_docletpath;
146
147   /**
148    * Option "-classpath": path to additional classes.
149    */
150   private String option_classpath;
151
152   /**
153    * Option "-sourcepath": path to the Java source files to be documented.
154    * FIXME: this should be a list of paths
155    */
156   private List option_sourcepath = new ArrayList();
157
158   /**
159    * Option "-extdirs": path to Java extension files.
160    */
161   private String option_extdirs;
162
163   /**
164    * Option "-verbose": Be verbose when generating documentation.
165    */
166   private boolean option_verbose;
167
168   /**
169    * Option "-nowarn": Do not print warnings.
170    */
171   private boolean option_nowarn;
172
173   /**
174    * Option "-locale:" Specify the locale charset of Java source files.
175    */
176   private Locale option_locale = new Locale("en", "us");
177
178   /**
179    * Option "-encoding": Specify character encoding of Java source files.
180    */
181   private String option_encoding;
182
183   /**
184    * Option "-J": Specify flags to be passed to Java runtime.
185    */
186   private List option_java_flags = new LinkedList(); //ArrayList();
187
188   /**
189    * Option "-source:" should be 1.4 to handle assertions, 1.1 is no
190    * longer supported.
191    */
192   private String option_source = "1.2";
193
194   /**
195    * Option "-subpackages": list of subpackages to be recursively
196    * added.
197    */
198   private List option_subpackages = new ArrayList();
199
200   /**
201    * Option "-exclude": list of subpackages to exclude.
202    */
203   private List option_exclude = new ArrayList();
204
205   /**
206    * Option "-breakiterator" - whether to use BreakIterator for
207    * detecting the end of the first sentence.
208    */
209   private boolean option_breakiterator;
210
211   /**
212    * Option "-licensetext" - whether to copy license text.
213    */
214   private boolean option_licensetext;
215
216   /**
217    * The locale-dependent collator used for sorting.
218    */
219   private Collator collator;
220
221   /**
222    * true when --version has been specified on the command line.
223    */
224   private boolean option_showVersion;
225   
226   /**
227    * true when -bootclasspath has been specified on the command line.
228    */
229   private boolean option_bootclasspath_specified;
230   
231   /**
232    * true when -all has been specified on the command line.
233    */
234   private boolean option_all;
235
236   /**
237    * true when -reflection has been specified on the command line.
238    */
239   private boolean option_reflection;
240   
241   // TODO: add the rest of the options as instance variables
242   
243   /**
244    * Parse all source files/packages and subsequentially start the Doclet given
245    * on the command line.
246    * 
247    * @param allOptions List of all command line tokens
248    */
249   private boolean startDoclet(List allOptions)
250   {
251
252     try
253     {
254
255       //--- Fetch the Class object for the Doclet.
256
257       Debug.log(1, "loading doclet class...");
258
259       Class docletClass;
260
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);
267         }
268         catch (Exception e) {
269           docletClass = Class.forName(option_doclet);
270         }
271       }
272       else {
273         docletClass = Class.forName(option_doclet);
274       }
275       //Object docletInstance = docletClass.newInstance();
276
277       Debug.log(1, "doclet class loaded...");
278
279       Method startTempMethod = null;
280       Method startMethod = null;
281       Method optionLenMethod = null;
282       Method validOptionsMethod = null;
283
284       //--- Try to find the optionLength method in the Doclet class.
285
286       try
287       {
288         optionLenMethod = docletClass.getMethod("optionLength", new Class[]
289           { String.class });
290       }
291       catch (NoSuchMethodException e)
292       {
293         // Ignore if not found; it's OK it the Doclet class doesn't define
294         // this method.
295       }
296
297       //--- Try to find the validOptions method in the Doclet class.
298
299       try
300       {
301         validOptionsMethod = docletClass.getMethod("validOptions", new Class[]
302           { String[][].class, DocErrorReporter.class });
303       }
304       catch (NoSuchMethodException e)
305       {
306         // Ignore if not found; it's OK it the Doclet class doesn't define
307         // this method.
308       }
309
310       //--- Find the start method in the Doclet class; complain if not found
311
312       try
313       {
314         startTempMethod = docletClass.getMethod("start", new Class[]
315           { TemporaryStore.class });
316       }
317       catch (Exception e)
318       {
319         // ignore
320       }
321       startMethod = docletClass.getMethod("start", new Class[]
322         { RootDoc.class });
323
324       //--- Feed the custom command line tokens to the Doclet
325
326       // stores all recognized options
327       List options = new LinkedList();
328
329       // stores packages and classes defined on the command line
330       List packageAndClasses = new LinkedList();
331
332       for (Iterator it = allOptions.iterator(); it.hasNext();)
333       {
334         String option = (String) it.next();
335
336         Debug.log(9, "parsing option '" + option + "'");
337
338         if (option.startsWith("-"))
339         {
340
341           //--- Parse option
342
343           int optlen = optionLength(option);
344
345           //--- Try to get option length from Doclet class
346
347           if (optlen <= 0 && optionLenMethod != null)
348           {
349
350             optionLenMethod.invoke(null, new Object[]
351               { option });
352
353             Debug.log(3, "invoking optionLen method");
354
355             optlen = ((Integer) optionLenMethod.invoke(null, new Object[]
356               { option })).intValue();
357
358             Debug.log(3, "done");
359           }
360
361           if (optlen <= 0) {
362
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;
370               }
371               else {
372                 String property = propertyValue.substring(0, ndx);
373                 String value = propertyValue.substring(ndx + 1);
374                 System.setProperty(property, value);
375               }
376             }
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.");
380             }
381             else {
382               //--- Complain if not found
383
384               reporter.printError("Unknown option " + option);
385               reporter.printNotice(STRING_TRY_GJDOC_HELP);
386               return false;
387             }
388           }
389           else
390           {
391
392             //--- Read option values
393
394             String[] optionAndValues = new String[optlen];
395             optionAndValues[0] = option;
396             for (int i = 1; i < optlen; ++i)
397             {
398               if (!it.hasNext())
399               {
400                 reporter.printError("Missing value for option " + option);
401                 return false;
402               }
403               else
404               {
405                 optionAndValues[i] = (String) it.next();
406               }
407             }
408
409             //--- Store option for processing later
410
411             options.add(optionAndValues);
412           }
413         }
414         else if (option.length() > 0)
415         {
416
417           //--- Add to list of packages/classes if not option or option
418           // value
419
420           packageAndClasses.add(option);
421         }
422       }
423
424       Debug.log(9, "options parsed...");
425
426       //--- For each package specified with the -subpackages option on
427       //         the command line, recursively find all valid java files
428       //         beneath it.
429
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
433
434       for (Iterator it = option_subpackages.iterator(); it.hasNext();)
435       {
436         String subpackage = (String) it.next();
437         Set foundPackages = new LinkedHashSet();
438
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);
443         }
444
445         addFoundPackages(subpackage, foundPackages);
446       }
447
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);
453         }
454         addFoundPackages(null, foundPackages);
455         for (Iterator packageIt = foundPackages.iterator(); packageIt.hasNext(); ) {
456           String packageName = (String)packageIt.next();
457           if (null == packageName) {
458             packageName = "";
459           }
460           rootDoc.addSpecifiedPackageName(packageName);
461         }
462       }
463
464       for (Iterator it = packageAndClasses.iterator(); it.hasNext();)
465       {
466
467         String classOrPackage = (String) it.next();
468
469         boolean foundSourceFile = false;
470
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;
479             }
480           }
481           if (!foundSourceFile) {
482             File sourceFile = new File(classOrPackage);
483             if (sourceFile.exists() && !sourceFile.isDirectory()) {
484               rootDoc.addSpecifiedSourceFile(sourceFile);
485               foundSourceFile = true;
486             } 
487           }
488         }
489
490         if (!foundSourceFile) {
491         //--- Check for illegal name
492
493         if (classOrPackage.startsWith(".")
494             || classOrPackage.endsWith(".")
495             || classOrPackage.indexOf("..") > 0
496             || !checkCharSet(classOrPackage,
497                 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_."))
498         {
499           throw new ParseException("Illegal class or package name '"
500               + classOrPackage + "'");
501         }
502
503         //--- Assemble absolute path to package
504
505         String classOrPackageRelPath = classOrPackage.replace('.',
506             File.separatorChar);
507
508         //--- Create one file object each for a possible package directory
509         //         and a possible class file, and find out if they exist.
510
511         List packageDirs = rootDoc.findSourceFiles(classOrPackageRelPath);
512         List sourceFiles = rootDoc.findSourceFiles(classOrPackageRelPath + ".java");
513
514         boolean packageDirExists = !packageDirs.isEmpty();
515         boolean sourceFileExists = !sourceFiles.isEmpty();
516
517         //--- Complain if neither exists: not found
518
519         if (!packageDirExists && !sourceFileExists)
520         {
521           reporter.printError("Class or package " + classOrPackage
522               + " not found.");
523           return false;
524         }
525
526         //--- Complain if both exist: ambigious
527
528         else
529           if (packageDirExists && sourceFileExists)
530           {
531             reporter.printError("Ambigious class/package name "
532                 + classOrPackage + ".");
533             return false;
534           }
535
536           //--- Otherwise, if the package directory exists, it is a package
537
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;
548                 }
549               }
550               if (!packageDirFound) {
551                 reporter.printError("No suitable file or directory found for" + classOrPackage);
552                 return false;
553               }
554             }
555
556             //--- Otherwise, emit error message
557
558             else {
559                 reporter.printError("No sources files found for package " + classOrPackage);
560             }
561         }
562       }
563
564       //--- Complain if no packages or classes specified
565
566       if (option_help) {
567         usage();
568         return true;
569       }
570
571       //--- Validate custom options passed on command line
572       //         by asking the Doclet if they are OK.
573
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())
579       {
580         // Not ok: shutdown system.
581         reporter.printNotice(STRING_TRY_GJDOC_HELP);
582         return false;
583       }
584
585       if (!rootDoc.hasSpecifiedPackagesOrClasses()) {
586         reporter.printError("No packages or classes specified.");
587         reporter.printNotice(STRING_TRY_GJDOC_HELP);
588         return false;
589       }
590
591       rootDoc.setOptions(customOptionArr);
592
593       rootDoc.build();
594
595       //--- Bail out if no classes found
596
597       if (0 == rootDoc.classes().length
598           && 0 == rootDoc.specifiedPackages().length
599           && 0 == rootDoc.specifiedClasses().length)
600       {
601         reporter.printError("No packages or classes found(!).");
602         return false;
603       }
604
605       //--- Our work is done, tidy up memory
606
607       System.gc();
608       System.gc();
609
610       //--- Set flag indicating Phase II of documentation generation
611
612       docletRunning = true;
613
614       //--- Invoke the start method on the Doclet: produce output
615
616       reporter.printNotice("Running doclet...");
617
618       TemporaryStore tstore = new TemporaryStore(Main.rootDoc);
619
620       Thread.currentThread().setContextClassLoader(docletClass.getClassLoader());
621
622       if (null != startTempMethod)
623       {
624         startTempMethod.invoke(null, new Object[]
625           { tstore });
626       }
627       else
628       {
629         startMethod.invoke(null, new Object[]
630           { tstore.getAndClear() });
631       }
632
633       //--- Let the user know how many warnings/errors occured
634
635       if (reporter.getWarningCount() > 0)
636       {
637         reporter.printNotice(reporter.getWarningCount() + " warnings");
638       }
639
640       if (reporter.getErrorCount() > 0)
641       {
642         reporter.printNotice(reporter.getErrorCount() + " errors");
643       }
644
645       System.gc();
646
647       //--- Done.
648       return true;
649     }
650     catch (Exception e)
651     {
652       e.printStackTrace();
653       return false;
654     }
655   }
656
657   private void addFoundPackages(String subpackage, Set foundPackages)
658   {        
659     if (foundPackages.isEmpty()) {
660       reporter.printWarning("No classes found under subpackage " + subpackage);
661     }
662     else {
663       boolean onePackageAdded = false;
664       for (Iterator rit = foundPackages.iterator(); rit.hasNext();) {
665         String foundPackage = (String)rit.next();
666         boolean excludeThisPackage = false;
667
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;
674           }
675         }
676
677         if (!excludeThisPackage) {
678           rootDoc.addSpecifiedPackageName(foundPackage);
679           onePackageAdded = true;
680         }
681       }
682       if (!onePackageAdded) {
683         if (null != subpackage) {
684           reporter.printWarning("No non-excluded classes found under subpackage " + subpackage);
685         }
686         else {
687           reporter.printWarning("No non-excluded classes found.");
688         }
689       }
690     }
691   }
692
693   /**
694    *  Verify that the given file is a valid Java source file and that
695    *  it specifies the given package.
696    */
697   private boolean isValidJavaFile(File file,
698                                   String expectedPackage)
699   {
700     try {
701       InputStream in = new BufferedInputStream(new FileInputStream(file));
702
703       int ch, prevChar = 0;
704
705       final int STATE_DEFAULT = 0;
706       final int STATE_COMMENT = 1;
707       final int STATE_LINE_COMMENT = 2;
708
709       int state = STATE_DEFAULT;
710
711       StringBuffer word = new StringBuffer();
712       int wordIndex = 0;
713
714       while ((ch = in.read()) >= 0) {
715         String completeWord = null;
716
717         switch (state) {
718         case STATE_COMMENT:
719           if (prevChar == '*' && ch == '/') {
720             state = STATE_DEFAULT;
721           }
722           break;
723
724         case STATE_LINE_COMMENT:
725           if (ch == '\n') {
726             state = STATE_DEFAULT;
727           }
728           break;
729
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);
736             }
737             state = STATE_COMMENT;
738           }
739           else if (prevChar == '/' && ch == '/') {
740             word.deleteCharAt(word.length() - 1);
741             if (word.length() > 0) {
742               completeWord = word.toString();
743               word.setLength(0);
744             }
745             state = STATE_LINE_COMMENT;
746           }
747           else if (" \t\r\n".indexOf(ch) >= 0) {
748             if (word.length() > 0) {
749               completeWord = word.toString();
750               word.setLength(0);
751             }
752           }
753           else if (1 == wordIndex && ';' == ch) {
754             if (word.length() > 0) {
755               completeWord = word.toString();
756               word.setLength(0);
757             }
758             else {
759               // empty package name in source file: "package ;" -> invalid source file
760               in.close();
761               return false;
762             }
763           }
764           else {
765             word.append((char)ch);
766           }
767           break;
768         }
769
770         if (null != completeWord) {
771           if (0 == wordIndex && !"package".equals(completeWord)) {
772             in.close();
773             return "".equals(expectedPackage);
774           }
775           else if (1 == wordIndex) {
776             in.close();
777             return expectedPackage.equals(completeWord);
778           }
779           ++ wordIndex;
780         }
781
782         prevChar = ch;
783       }
784
785       // no package or class found before end-of-file -> invalid source file
786
787       in.close();
788       return false;
789     }
790     catch (IOException e) {
791       reporter.printWarning("Could not examine file " + file + ": " + e);
792       return false;
793     }
794   }
795
796   /**
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.
800    */
801   private void findPackages(String subpackage, 
802                             File packageDir, 
803                             Set result)
804   {
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);
813             }
814             else {
815               result.add(subpackage);
816             }
817             break;
818           }
819         }
820       }
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();
827           }
828           else {
829             newSubpackage = file.getName();
830           }
831           findPackages(newSubpackage, file, result);
832         }
833       }
834     }
835   }
836
837   /**
838    *
839    */
840   private static boolean validOptions(String options[][],
841       DocErrorReporter reporter)
842   {
843
844     boolean foundDocletOption = false;
845     for (int i = 0; i < options.length; i++)
846     {
847       String[] opt = options[i];
848       if (opt[0].equalsIgnoreCase("-doclet"))
849       {
850         if (foundDocletOption)
851         {
852           reporter.printError("Only one -doclet option allowed.");
853           return false;
854         }
855         else
856         {
857           foundDocletOption = true;
858         }
859       }
860     }
861
862     return true;
863   }
864
865   /**
866    * Main entry point. This is the method called when gjdoc is invoked from the
867    * command line.
868    * 
869    * @param args
870    *          command line arguments
871    */
872   public static void main(String[] args)
873   {
874
875     try
876     {
877       //--- Remember current time for profiling purposes
878
879       Timer.setStartTime();
880
881       //--- Handle control to the Singleton instance of this class
882
883       int result = instance.start(args);
884
885       if (result < 0) {
886         // fatal error
887         System.exit(5);
888       }
889       else if (result > 0) {
890         // errors encountered
891         System.exit(1);
892       }
893       else {
894         // success
895         System.exit(0);
896       }
897     }
898     catch (Exception e)
899     {
900       //--- unexpected error
901       e.printStackTrace();
902       System.exit(1);
903     }
904   }
905
906   /**
907    * Parses command line arguments and subsequentially handles control to the
908    * startDoclet() method
909    *   
910    * @param args The command line parameters.
911    */
912    public static int execute(String[] args)
913    {
914      try
915      {
916        int result = instance.start(args);
917        if (result < 0) {
918          // fatal error
919          return 5;
920        }
921        else if (result > 0) {
922          // errors encountered
923          return 1;
924        }
925        else {
926          // success
927          return 0;
928        }
929      }
930      catch (Exception e)
931      {
932        // unexpected error
933        return 1;
934      }
935    }
936
937   /**
938    * @param programName Name of the program (for error messages). *disregarded*
939    * @param args The command line parameters.
940    * @returns The return code.
941    */
942   public static int execute(String programName,
943                             String[] args)
944   {
945     return execute(args);
946   }
947  
948   /**
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)
957   {
958     // not yet implemented
959   }*/
960  
961   /**
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)
970   {
971     // not yet implemented
972   }*/
973  
974   /**
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)
989   {
990     // not yet implemented
991   }*/
992
993   /**
994    * Parses command line arguments and subsequentially handles control to the
995    * startDoclet() method
996    * 
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
1005    */
1006   public int start(String[] args) throws ParseException, IOException
1007   {
1008
1009     //--- Collect unparsed arguments in array and resolve references
1010     //         to external argument files.
1011
1012     List arguments = new ArrayList(args.length);
1013
1014     for (int i = 0; i < args.length; ++i)
1015     {
1016       if (!args[i].startsWith("@"))
1017       {
1018         arguments.add(args[i]);
1019       }
1020       else
1021       {
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)
1033         {
1034           arguments.add(st.sval);
1035         }
1036       }
1037     }
1038
1039     //--- Initialize Map for option parsing
1040
1041     initOptions();
1042
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.
1047
1048     List options = new LinkedList();
1049
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.
1055
1056     List customOptions = new LinkedList();
1057
1058     rootDoc = new RootDocImpl();
1059     reporter = rootDoc.getReporter();
1060
1061     //--- Iterate over all options given on the command line
1062
1063     for (Iterator it = arguments.iterator(); it.hasNext();)
1064     {
1065
1066       String arg = (String) it.next();
1067
1068       //--- Check if gjdoc recognizes this option as a standard option
1069       //         and remember the options' argument count
1070
1071       int optlen = optionLength(arg);
1072
1073       //--- Argument count == 0 indicates that the option is not recognized.
1074       //         Add it to the list of custom option tokens
1075
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.
1080
1081       if (optlen > 0)
1082       {
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)
1087         {
1088           if (it.hasNext())
1089           {
1090             option[j] = (String) it.next();
1091             if (option[j].startsWith("-"))
1092             {
1093               optargs_ok = false;
1094             }
1095           }
1096           else
1097           {
1098             optargs_ok = false;
1099           }
1100         }
1101         if (optargs_ok)
1102           options.add(option);
1103         else
1104         {
1105           //         If the option requires more arguments than given on the
1106           //         command line, issue a fatal error
1107
1108           reporter.printFatal("Missing value for option " + arg + ".");
1109         }
1110       }
1111     }
1112
1113     //--- Create an array of String arrays from the dynamic array built above
1114
1115     String[][] optionArr = (String[][]) options.toArray(new String[options
1116         .size()][0]);
1117
1118     //--- Validate all options and issue warnings/errors
1119
1120     if (validOptions(optionArr, rootDoc))
1121     {
1122
1123       //--- We got valid options; parse them and store the parsed values
1124       //         in 'option_*' fields.
1125
1126       readOptions(optionArr);
1127
1128       //--- Show version and exit if requested by user
1129
1130       if (option_showVersion) {
1131         System.out.println("gjdoc " + getGjdocVersion());
1132         System.exit(0);
1133       }
1134
1135       if (option_bootclasspath_specified) {
1136         reporter.printWarning("-bootclasspath ignored: not supported by"
1137                               + " gjdoc wrapper script, or no wrapper script in use.");
1138       }
1139
1140       // If we have an empty source path list, add the current directory ('.')
1141
1142       if (option_sourcepath.size() == 0)
1143         option_sourcepath.add(new File("."));
1144
1145       //--- We have all information we need to start the doclet at this time
1146     
1147       if (null != option_encoding) {
1148         rootDoc.setSourceEncoding(option_encoding);
1149       }
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"));
1154       }
1155       rootDoc.setSourcePath(option_sourcepath);
1156
1157       //addJavaLangClasses();
1158
1159       if (!startDoclet(arguments)) {
1160         return -1;
1161       }
1162     }
1163
1164     return reporter.getErrorCount();
1165   }
1166
1167   private void addJavaLangClasses()
1168     throws IOException
1169   {
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) {
1175       
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);
1184       }
1185     }
1186   }
1187
1188   /**
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.
1192    */
1193   private abstract class OptionProcessor
1194   {
1195
1196     /**
1197      * Number of arguments expected by this option.
1198      */
1199     private int argCount;
1200
1201     /**
1202      * Initializes this instance.
1203      * 
1204      * @param argCount
1205      *          number of arguments
1206      */
1207     public OptionProcessor(int argCount)
1208     {
1209       this.argCount = argCount;
1210     }
1211
1212     /**
1213      * Overridden by derived classes with behavior to parse the arguments
1214      * specified with this option.
1215      * 
1216      * @param args
1217      *          command line arguments
1218      */
1219     abstract void process(String[] args);
1220   }
1221
1222   /**
1223    * Maps option tags (e.g. '-sourcepath') to OptionProcessor objects.
1224    * Initialized only once by method initOptions(). FIXME: Rename to
1225    * 'optionProcessors'.
1226    */
1227   private static Map options = null;
1228
1229   /**
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.
1233    */
1234   private void initOptions()
1235   {
1236
1237     options = new HashMap();
1238
1239     //--- Put one OptionProcessor object into the map
1240     //         for each option recognized.
1241
1242     options.put("-overview", new OptionProcessor(2)
1243       {
1244
1245         void process(String[] args)
1246         {
1247           option_overview = args[0];
1248         }
1249       });
1250     options.put("-public", new OptionProcessor(1)
1251       {
1252
1253         void process(String[] args)
1254         {
1255           option_coverage = COVERAGE_PUBLIC;
1256         }
1257       });
1258     options.put("-protected", new OptionProcessor(1)
1259       {
1260
1261         void process(String[] args)
1262         {
1263           option_coverage = COVERAGE_PROTECTED;
1264         }
1265       });
1266     options.put("-package", new OptionProcessor(1)
1267       {
1268
1269         void process(String[] args)
1270         {
1271           option_coverage = COVERAGE_PACKAGE;
1272         }
1273       });
1274     options.put("-private", new OptionProcessor(1)
1275       {
1276
1277         void process(String[] args)
1278         {
1279           option_coverage = COVERAGE_PRIVATE;
1280         }
1281       });
1282     OptionProcessor helpProcessor = new OptionProcessor(1)
1283       {
1284
1285         void process(String[] args)
1286         {
1287           option_help = true;
1288         }
1289       };
1290
1291     options.put("-help", helpProcessor);
1292     options.put("--help", helpProcessor);
1293     options.put("-doclet", new OptionProcessor(2)
1294         {
1295
1296           void process(String[] args)
1297           {
1298             option_doclet = args[0];
1299           }
1300         });
1301     options.put("-docletpath", new OptionProcessor(2)
1302         {
1303
1304           void process(String[] args)
1305           {
1306             option_docletpath = args[0];
1307           }
1308         });
1309     options.put("-nowarn", new OptionProcessor(1)
1310         {
1311
1312           void process(String[] args)
1313           {
1314             option_nowarn = true;
1315           }
1316         });
1317     options.put("-source", new OptionProcessor(2)
1318         {
1319
1320           void process(String[] args)
1321           {
1322             option_source = args[0];
1323             if (!"1.2".equals(option_source) 
1324                 && !"1.3".equals(option_source)
1325                 && !"1.4".equals(option_source)) {
1326
1327               throw new RuntimeException("Only he following values are currently"
1328                                          + " supported for option -source: 1.2, 1.3, 1.4.");
1329             }
1330           }
1331         });
1332     OptionProcessor sourcePathProcessor = new OptionProcessor(2) {
1333         void process(String[] args)
1334         {
1335           Debug.log(1, "-sourcepath is '" + args[0] + "'");
1336           for (StringTokenizer st = new StringTokenizer(args[0],
1337               File.pathSeparator); st.hasMoreTokens();)
1338           {
1339             String path = st.nextToken();
1340             File file = new File(path);
1341             if (!(file.exists()))
1342             {
1343               throw new RuntimeException("The source path " + path
1344                   + " does not exist.");
1345             }
1346             option_sourcepath.add(file);
1347           }
1348         }
1349       };
1350     options.put("-s", sourcePathProcessor);
1351     options.put("-sourcepath", sourcePathProcessor);
1352     options.put("-subpackages", new OptionProcessor(2)
1353       {
1354         void process(String[] args)
1355         {
1356           StringTokenizer st = new StringTokenizer(args[0], ":"); 
1357           while (st.hasMoreTokens()) {
1358             String packageName = st.nextToken();
1359
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 + "'");
1367             }
1368             option_subpackages.add(packageName);
1369           }
1370         }
1371       });
1372     options.put("-exclude", new OptionProcessor(2)
1373       {
1374         void process(String[] args)
1375         {
1376           StringTokenizer st = new StringTokenizer(args[0], ":"); 
1377           while (st.hasMoreTokens()) {
1378             String packageName = st.nextToken();
1379
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 + "'");
1387             }
1388             option_exclude.add(packageName);
1389           }
1390         }
1391       });
1392     // TODO include other options here
1393     options.put("-verbose", new OptionProcessor(1)
1394       {
1395
1396         void process(String[] args)
1397         {
1398           option_verbose = true;
1399           System.err.println("WARNING: Unsupported option -verbose ignored");
1400         }
1401       });
1402     options.put("-quiet", new OptionProcessor(1)
1403       {
1404
1405         void process(String[] args)
1406         {
1407           reporter.setQuiet(true);
1408         }
1409       });
1410     options.put("-locale", new OptionProcessor(2)
1411       {
1412
1413         void process(String[] args)
1414         {
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();
1422           }
1423           if (st.hasMoreTokens()) {
1424             country = st.nextToken();
1425           }
1426           if (st.hasMoreTokens()) {
1427             variant = st.nextToken();
1428           }
1429           if (variant != null) {
1430             option_locale = new Locale(language, country, variant);
1431           }
1432           else if (country != null) {
1433              option_locale = new Locale(language, country);
1434           }
1435           else if (language != null) {
1436              option_locale = new Locale(language);
1437           }
1438           else {
1439               throw new RuntimeException("Illegal locale specification '"
1440                                          + localeName + "'");
1441           }
1442         }
1443       });
1444     options.put("-encoding", new OptionProcessor(2)
1445       {
1446
1447         void process(String[] args)
1448         {
1449           option_encoding = args[0];
1450         }
1451       });
1452     options.put("-breakiterator", new OptionProcessor(1)
1453       {
1454         void process(String[] args)
1455         {
1456           option_breakiterator = true;
1457         }
1458       });
1459     options.put("-licensetext", new OptionProcessor(1)
1460       {
1461         void process(String[] args)
1462         {
1463           option_licensetext = true;
1464         }
1465       });
1466     options.put("-overview", new OptionProcessor(2)
1467       {
1468         void process(String[] args)
1469         {
1470           try {
1471             getRootDoc().setRawCommentText(RootDocImpl.readHtmlBody(new File(args[0])));
1472           }
1473           catch (IOException e) { 
1474             throw new RuntimeException("Cannot read file specified in option -overview: " + e.getMessage());
1475           }
1476         }
1477       });
1478     options.put("-classpath", new OptionProcessor(2)
1479       {
1480         void process(String[] args)
1481         {
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]);
1485         }
1486       });
1487     options.put("--version", new OptionProcessor(1)
1488       {
1489         void process(String[] args)
1490         {
1491           option_showVersion = true;
1492         }
1493       });
1494     options.put("-bootclasspath", new OptionProcessor(1)
1495       {
1496         void process(String[] args)
1497         {
1498           option_bootclasspath_specified = true;
1499         }
1500       });
1501     options.put("-all", new OptionProcessor(1)
1502       {
1503         void process(String[] args)
1504         {
1505           option_all = true;
1506         }
1507       });
1508     options.put("-reflection", new OptionProcessor(1)
1509       {
1510         void process(String[] args)
1511         {
1512           option_reflection = true;
1513         }
1514       });
1515   }
1516
1517   /**
1518    * Determine how many arguments the given option requires.
1519    * 
1520    * @param option
1521    *          The name of the option without leading dash.
1522    */
1523   private static int optionLength(String option)
1524   {
1525
1526     OptionProcessor op = (OptionProcessor) options.get(option.toLowerCase());
1527     if (op != null)
1528       return op.argCount;
1529     else
1530       return 0;
1531   }
1532
1533   /**
1534    * Process all given options. Assumes that the options have been validated
1535    * before.
1536    * 
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.
1540    */
1541   private void readOptions(String[][] optionArr)
1542   {
1543
1544     //--- For each option, find the appropriate OptionProcessor
1545     //        and call its process() method
1546
1547     for (int i = 0; i < optionArr.length; ++i)
1548     {
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);
1554     }
1555   }
1556
1557   /**
1558    * Print command line usage.
1559    */
1560   private static void usage()
1561   {
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"
1649                /**
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"
1679                **/
1680             );
1681   }
1682
1683   /**
1684    * The root of the gjdoc tool.
1685    * 
1686    * @return all the options of the gjdoc application.
1687    */
1688   public static RootDocImpl getRootDoc()
1689   {
1690     return rootDoc;
1691   }
1692
1693   /**
1694    * Get the gjdoc singleton.
1695    * 
1696    * @return the gjdoc instance.
1697    */
1698   public static Main getInstance()
1699   {
1700     return instance;
1701   }
1702
1703   /**
1704    * Is this access level covered?
1705    * 
1706    * @param accessLevel
1707    *          the access level we want to know if it is covered.
1708    * @return true if the access level is covered.
1709    */
1710   public boolean includeAccessLevel(int accessLevel)
1711   {
1712     return coverageTemplates[option_coverage][accessLevel];
1713   }
1714
1715   /**
1716    * Is the doclet running?
1717    * 
1718    * @return true if it's running
1719    */
1720   public boolean isDocletRunning()
1721   {
1722     return docletRunning;
1723   }
1724
1725   /**
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.
1729    * 
1730    * @param toCheck
1731    *          the charset to check.
1732    * @param charSet
1733    *          the reference charset
1734    * @return true if they match.
1735    */
1736   public static boolean checkCharSet(String toCheck, String charSet)
1737   {
1738     for (int i = 0; i < toCheck.length(); ++i)
1739     {
1740       if (charSet.indexOf(toCheck.charAt(i)) < 0)
1741         return false;
1742     }
1743     return true;
1744   }
1745
1746   /**
1747    * Makes the RootDoc eligible for the GC.
1748    */
1749   public static void releaseRootDoc()
1750   {
1751     rootDoc.flush();
1752   }
1753
1754   /**
1755    * Return whether the -breakiterator option has been specified.
1756    */
1757   public boolean isUseBreakIterator()
1758   {
1759     return this.option_breakiterator
1760       || !getLocale().getLanguage().equals(Locale.ENGLISH.getLanguage());
1761   }
1762
1763   /**
1764    * Return whether boilerplate license text should be copied.
1765    */
1766   public boolean isCopyLicenseText()
1767   {
1768     return this.option_licensetext;
1769   }
1770
1771   /**
1772    *  Return the locale specified using the -locale option or the
1773    *  default locale;
1774    */
1775   public Locale getLocale()
1776   {
1777     return this.option_locale;
1778   }
1779
1780   /**
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.
1784    */
1785   public Collator getCollator()
1786   {
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                                 + ".");
1800         }
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.");
1808         }
1809       }
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.");
1815       }
1816     }
1817     return this.collator;
1818   }
1819
1820   public boolean isCacheRawComments()
1821   {
1822     return true;
1823   }
1824
1825   public String getGjdocVersion()
1826   {
1827     if (null == gjdocVersion) {
1828       try {
1829         Properties versionProperties = new Properties();
1830         versionProperties.load(getClass().getResourceAsStream("version.properties"));
1831         gjdocVersion = versionProperties.getProperty("gjdoc.version");
1832       }
1833       catch (IOException ignore) {
1834       }
1835       if (null == gjdocVersion) {
1836         gjdocVersion = "unknown";
1837       }
1838     }
1839     return gjdocVersion;
1840   }
1841
1842   public boolean isReflectionEnabled()
1843   {
1844     return this.option_reflection;
1845   }
1846 }