OSDN Git Service

2013-01-10 Matthias Klose <doko@ubuntu.com>
[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 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38 package gnu.classpath.tools.gjdoc;
39
40 import com.sun.javadoc.*;
41 import java.io.*;
42 import java.util.*;
43 import java.lang.reflect.*;
44 import java.text.Collator;
45
46 import gnu.classpath.tools.FileSystemClassLoader;
47
48 /**
49  * Class that will launch the gjdoc tool.
50  */
51 public final class Main
52 {
53
54   /**
55    * Do we load classes that are referenced as base class?
56    */
57   static final boolean DESCEND_SUPERCLASS = true;
58
59   /**
60    * Do we load classes that are referenced as interface?
61    */
62   static final boolean DESCEND_INTERFACES = false;
63
64   /**
65    * Do we load classes that are imported in a source file?
66    */
67   static final boolean DESCEND_IMPORTED = true;
68
69   /**
70    * Document only public members.
71    */
72   static final int COVERAGE_PUBLIC = 0;
73
74   /**
75    * Document only public and protected members.
76    */
77   static final int COVERAGE_PROTECTED = 1;
78
79   /**
80    * Document public, protected and package private members.
81    */
82   static final int COVERAGE_PACKAGE = 2;
83
84   /**
85    * Document all members.
86    */
87   static final int COVERAGE_PRIVATE = 3;
88
89   /*
90    *  FIXME: This should come from a ResourceBundle
91    */
92   private static final String STRING_TRY_GJDOC_HELP =
93      "Try `gjdoc --help' for more information.";
94
95   /**
96    * Grid for looking up whether a particular access level is included in the
97    * documentation.
98    */
99   static final boolean[][] coverageTemplates = new boolean[][]
100     { new boolean[]
101       { true, false, false, false }, // public
102         new boolean[]
103           { true, true, false, false }, // protected
104         new boolean[]
105           { true, true, true, false }, // package
106         new boolean[]
107           { true, true, true, true }, // private
108     };
109
110   /**
111    * Holds the Singleton instance of this class.
112    */
113   private static Main instance = new Main();
114
115   /**
116    * Avoid re-instantiation of this class.
117    */
118   private Main()
119   {
120   }
121
122   private static RootDocImpl rootDoc;
123
124   private ErrorReporter reporter;
125
126   /**
127    * Cache for version string from resource /version.properties
128    */
129   private String gjdocVersion;
130
131   /**
132    * <code>false</code> during Phase I: preparation of the documentation data.
133    * <code>true</code> during Phase II: documentation output by doclet.
134    */
135   boolean docletRunning = false;
136
137   //---- Command line options
138
139   /**
140    * Option "-doclet": name of the Doclet class to use.
141    */
142   private String option_doclet = "gnu.classpath.tools.doclets.htmldoclet.HtmlDoclet";
143
144   /**
145    * Option "-overview": path to the special overview file.
146    */
147   private String option_overview;
148
149   /**
150    * Option "-coverage": which members to include in generated documentation.
151    */
152   private int option_coverage = COVERAGE_PROTECTED;
153
154   /**
155    * Option "-help": display command line usage.
156    */
157   private boolean option_help;
158
159   /**
160    * Option "-docletpath": path to doclet classes.
161    */
162   private String option_docletpath;
163
164   /**
165    * Option "-classpath": path to additional classes.
166    */
167   private String option_classpath;
168
169   /**
170    * Option "-sourcepath": path to the Java source files to be documented.
171    * FIXME: this should be a list of paths
172    */
173   private List option_sourcepath = new ArrayList();
174
175   /**
176    * Option "-extdirs": path to Java extension files.
177    */
178   private String option_extdirs;
179
180   /**
181    * Option "-verbose": Be verbose when generating documentation.
182    */
183   private boolean option_verbose;
184
185   /**
186    * Option "-nowarn": Do not print warnings.
187    */
188   private boolean option_nowarn;
189
190   /**
191    * Option "-locale:" Specify the locale charset of Java source files.
192    */
193   private Locale option_locale = new Locale("en", "us");
194
195   /**
196    * Option "-encoding": Specify character encoding of Java source files.
197    */
198   private String option_encoding;
199
200   /**
201    * Option "-J": Specify flags to be passed to Java runtime.
202    */
203   private List option_java_flags = new LinkedList(); //ArrayList();
204
205   /**
206    * Option "-source:" should be 1.4 to handle assertions, 1.1 is no
207    * longer supported.
208    */
209   private String option_source = "1.2";
210
211   /**
212    * Option "-subpackages": list of subpackages to be recursively
213    * added.
214    */
215   private List option_subpackages = new ArrayList();
216
217   /**
218    * Option "-exclude": list of subpackages to exclude.
219    */
220   private List option_exclude = new ArrayList();
221
222   /**
223    * Option "-breakiterator" - whether to use BreakIterator for
224    * detecting the end of the first sentence.
225    */
226   private boolean option_breakiterator;
227
228   /**
229    * Option "-licensetext" - whether to copy license text.
230    */
231   private boolean option_licensetext;
232
233   /**
234    * The locale-dependent collator used for sorting.
235    */
236   private Collator collator;
237
238   /**
239    * true when --version has been specified on the command line.
240    */
241   private boolean option_showVersion;
242
243   /**
244    * true when -bootclasspath has been specified on the command line.
245    */
246   private boolean option_bootclasspath_specified;
247
248   /**
249    * true when -all has been specified on the command line.
250    */
251   private boolean option_all;
252
253   /**
254    * true when -reflection has been specified on the command line.
255    */
256   private boolean option_reflection;
257
258   // TODO: add the rest of the options as instance variables
259
260   /**
261    * Parse all source files/packages and subsequentially start the Doclet given
262    * on the command line.
263    *
264    * @param allOptions List of all command line tokens
265    */
266   private boolean startDoclet(List allOptions)
267   {
268
269     try
270     {
271
272       //--- Fetch the Class object for the Doclet.
273
274       Debug.log(1, "loading doclet class...");
275
276       Class docletClass;
277
278       if (null != option_docletpath) {
279         try {
280           FileSystemClassLoader docletPathClassLoader
281             = new FileSystemClassLoader(option_docletpath);
282           System.err.println("trying to load class  " + option_doclet + " from path " + option_docletpath);
283           docletClass = docletPathClassLoader.findClass(option_doclet);
284         }
285         catch (Exception e) {
286           docletClass = Class.forName(option_doclet);
287         }
288       }
289       else {
290         docletClass = Class.forName(option_doclet);
291       }
292       //Object docletInstance = docletClass.newInstance();
293
294       Debug.log(1, "doclet class loaded...");
295
296       Method startTempMethod = null;
297       Method startMethod = null;
298       Method optionLenMethod = null;
299       Method validOptionsMethod = null;
300
301       //--- Try to find the optionLength method in the Doclet class.
302
303       try
304       {
305         optionLenMethod = docletClass.getMethod("optionLength", new Class[]
306           { String.class });
307       }
308       catch (NoSuchMethodException e)
309       {
310         // Ignore if not found; it's OK it the Doclet class doesn't define
311         // this method.
312       }
313
314       //--- Try to find the validOptions method in the Doclet class.
315
316       try
317       {
318         validOptionsMethod = docletClass.getMethod("validOptions", new Class[]
319           { String[][].class, DocErrorReporter.class });
320       }
321       catch (NoSuchMethodException e)
322       {
323         // Ignore if not found; it's OK it the Doclet class doesn't define
324         // this method.
325       }
326
327       //--- Find the start method in the Doclet class; complain if not found
328
329       try
330       {
331         startTempMethod = docletClass.getMethod("start", new Class[]
332           { TemporaryStore.class });
333       }
334       catch (Exception e)
335       {
336         // ignore
337       }
338       startMethod = docletClass.getMethod("start", new Class[]
339         { RootDoc.class });
340
341       //--- Feed the custom command line tokens to the Doclet
342
343       // stores all recognized options
344       List options = new LinkedList();
345
346       // stores packages and classes defined on the command line
347       List packageAndClasses = new LinkedList();
348
349       for (Iterator it = allOptions.iterator(); it.hasNext();)
350       {
351         String option = (String) it.next();
352
353         Debug.log(9, "parsing option '" + option + "'");
354
355         if (option.startsWith("-"))
356         {
357
358           //--- Parse option
359
360           int optlen = optionLength(option);
361
362           //--- Try to get option length from Doclet class
363
364           if (optlen <= 0 && optionLenMethod != null)
365           {
366
367             optionLenMethod.invoke(null, new Object[]
368               { option });
369
370             Debug.log(3, "invoking optionLen method");
371
372             optlen = ((Integer) optionLenMethod.invoke(null, new Object[]
373               { option })).intValue();
374
375             Debug.log(3, "done");
376           }
377
378           if (optlen <= 0) {
379
380             if (option.startsWith("-JD")) {
381               // Simulate VM option -D
382               String propertyValue = option.substring(3);
383               int ndx = propertyValue.indexOf('=');
384               if (ndx <= 0) {
385                 reporter.printError("Illegal format in option " + option + ": use -JDproperty=value");
386                 return false;
387               }
388               else {
389                 String property = propertyValue.substring(0, ndx);
390                 String value = propertyValue.substring(ndx + 1);
391                 System.setProperty(property, value);
392               }
393             }
394             else if (option.startsWith("-J")) {
395               //--- Warn if VM option is encountered
396               reporter.printWarning("Ignored option " + option + ". Pass this option to the VM if required.");
397             }
398             else {
399               //--- Complain if not found
400
401               reporter.printError("Unknown option " + option);
402               reporter.printNotice(STRING_TRY_GJDOC_HELP);
403               return false;
404             }
405           }
406           else
407           {
408
409             //--- Read option values
410
411             String[] optionAndValues = new String[optlen];
412             optionAndValues[0] = option;
413             for (int i = 1; i < optlen; ++i)
414             {
415               if (!it.hasNext())
416               {
417                 reporter.printError("Missing value for option " + option);
418                 return false;
419               }
420               else
421               {
422                 optionAndValues[i] = (String) it.next();
423               }
424             }
425
426             //--- Store option for processing later
427
428             options.add(optionAndValues);
429           }
430         }
431         else if (option.length() > 0)
432         {
433
434           //--- Add to list of packages/classes if not option or option
435           // value
436
437           packageAndClasses.add(option);
438         }
439       }
440
441       Debug.log(9, "options parsed...");
442
443       //--- For each package specified with the -subpackages option on
444       //         the command line, recursively find all valid java files
445       //         beneath it.
446
447       //--- For each class or package specified on the command line,
448       //         check that it exists and find out whether it is a class
449       //         or a package
450
451       for (Iterator it = option_subpackages.iterator(); it.hasNext();)
452       {
453         String subpackage = (String) it.next();
454         Set foundPackages = new LinkedHashSet();
455
456         for (Iterator pit = option_sourcepath.iterator(); pit.hasNext(); ) {
457           File sourceDir = (File)pit.next();
458           File packageDir = new File(sourceDir, subpackage.replace('.', File.separatorChar));
459           findPackages(subpackage, packageDir, foundPackages);
460         }
461
462         addFoundPackages(subpackage, foundPackages);
463       }
464
465       if (option_all) {
466         Set foundPackages = new LinkedHashSet();
467         for (Iterator pit = option_sourcepath.iterator(); pit.hasNext(); ) {
468           File sourceDir = (File)pit.next();
469           findPackages("", sourceDir, foundPackages);
470         }
471         addFoundPackages(null, foundPackages);
472         for (Iterator packageIt = foundPackages.iterator(); packageIt.hasNext(); ) {
473           String packageName = (String)packageIt.next();
474           if (null == packageName) {
475             packageName = "";
476           }
477           rootDoc.addSpecifiedPackageName(packageName);
478         }
479       }
480
481       for (Iterator it = packageAndClasses.iterator(); it.hasNext();)
482       {
483
484         String classOrPackage = (String) it.next();
485
486         boolean foundSourceFile = false;
487
488         if (classOrPackage.endsWith(".java")) {
489           for (Iterator pit = option_sourcepath.iterator(); pit.hasNext() && !foundSourceFile; ) {
490             File sourceDir = (File)pit.next();
491             File sourceFile = new File(sourceDir, classOrPackage);
492             if (sourceFile.exists() && !sourceFile.isDirectory()) {
493               rootDoc.addSpecifiedSourceFile(sourceFile);
494               foundSourceFile = true;
495               break;
496             }
497           }
498           if (!foundSourceFile) {
499             File sourceFile = new File(classOrPackage);
500             if (sourceFile.exists() && !sourceFile.isDirectory()) {
501               rootDoc.addSpecifiedSourceFile(sourceFile);
502               foundSourceFile = true;
503             }
504           }
505         }
506
507         if (!foundSourceFile) {
508         //--- Check for illegal name
509
510         if (classOrPackage.startsWith(".")
511             || classOrPackage.endsWith(".")
512             || classOrPackage.indexOf("..") > 0
513             || !checkCharSet(classOrPackage,
514                 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_."))
515         {
516           throw new ParseException("Illegal class or package name '"
517               + classOrPackage + "'");
518         }
519
520         //--- Assemble absolute path to package
521
522         String classOrPackageRelPath = classOrPackage.replace('.',
523             File.separatorChar);
524
525         //--- Create one file object each for a possible package directory
526         //         and a possible class file, and find out if they exist.
527
528         List packageDirs = rootDoc.findSourceFiles(classOrPackageRelPath);
529         List sourceFiles = rootDoc.findSourceFiles(classOrPackageRelPath + ".java");
530
531         boolean packageDirExists = !packageDirs.isEmpty();
532         boolean sourceFileExists = !sourceFiles.isEmpty();
533
534         //--- Complain if neither exists: not found
535
536         if (!packageDirExists && !sourceFileExists)
537         {
538           reporter.printError("Class or package " + classOrPackage
539               + " not found.");
540           return false;
541         }
542
543         //--- Complain if both exist: ambigious
544
545         else
546           if (packageDirExists && sourceFileExists)
547           {
548             reporter.printError("Ambigious class/package name "
549                 + classOrPackage + ".");
550             return false;
551           }
552
553           //--- Otherwise, if the package directory exists, it is a package
554
555           else
556             if (packageDirExists) {
557               Iterator packageDirIt = packageDirs.iterator();
558               boolean packageDirFound = false;
559               while (packageDirIt.hasNext()) {
560                 File packageDir = (File)packageDirIt.next();
561                 if (packageDir.isDirectory()) {
562                   rootDoc.addSpecifiedPackageName(classOrPackage);
563                   packageDirFound = true;
564                   break;
565                 }
566               }
567               if (!packageDirFound) {
568                 reporter.printError("No suitable file or directory found for" + classOrPackage);
569                 return false;
570               }
571             }
572
573             //--- Otherwise, emit error message
574
575             else {
576                 reporter.printError("No sources files found for package " + classOrPackage);
577             }
578         }
579       }
580
581       //--- Complain if no packages or classes specified
582
583       if (option_help) {
584         usage();
585         return true;
586       }
587
588       //--- Validate custom options passed on command line
589       //         by asking the Doclet if they are OK.
590
591       String[][] customOptionArr = (String[][]) options
592           .toArray(new String[0][0]);
593       if (validOptionsMethod != null
594           && !((Boolean) validOptionsMethod.invoke(null, new Object[]
595             { customOptionArr, reporter })).booleanValue())
596       {
597         // Not ok: shutdown system.
598         reporter.printNotice(STRING_TRY_GJDOC_HELP);
599         return false;
600       }
601
602       if (!rootDoc.hasSpecifiedPackagesOrClasses()) {
603         reporter.printError("No packages or classes specified.");
604         reporter.printNotice(STRING_TRY_GJDOC_HELP);
605         return false;
606       }
607
608       rootDoc.setOptions(customOptionArr);
609
610       rootDoc.build();
611
612       //--- Bail out if no classes found
613
614       if (0 == rootDoc.classes().length
615           && 0 == rootDoc.specifiedPackages().length
616           && 0 == rootDoc.specifiedClasses().length)
617       {
618         reporter.printError("No packages or classes found(!).");
619         return false;
620       }
621
622       //--- Our work is done, tidy up memory
623
624       System.gc();
625       System.gc();
626
627       //--- Set flag indicating Phase II of documentation generation
628
629       docletRunning = true;
630
631       //--- Invoke the start method on the Doclet: produce output
632
633       reporter.printNotice("Running doclet...");
634
635       TemporaryStore tstore = new TemporaryStore(Main.rootDoc);
636
637       Thread.currentThread().setContextClassLoader(docletClass.getClassLoader());
638
639       if (null != startTempMethod)
640       {
641         startTempMethod.invoke(null, new Object[]
642           { tstore });
643       }
644       else
645       {
646         startMethod.invoke(null, new Object[]
647           { tstore.getAndClear() });
648       }
649
650       //--- Let the user know how many warnings/errors occured
651
652       if (reporter.getWarningCount() > 0)
653       {
654         reporter.printNotice(reporter.getWarningCount() + " warnings");
655       }
656
657       if (reporter.getErrorCount() > 0)
658       {
659         reporter.printNotice(reporter.getErrorCount() + " errors");
660       }
661
662       System.gc();
663
664       //--- Done.
665       return true;
666     }
667     catch (Exception e)
668     {
669       e.printStackTrace();
670       return false;
671     }
672   }
673
674   private void addFoundPackages(String subpackage, Set foundPackages)
675   {
676     if (foundPackages.isEmpty()) {
677       reporter.printWarning("No classes found under subpackage " + subpackage);
678     }
679     else {
680       boolean onePackageAdded = false;
681       for (Iterator rit = foundPackages.iterator(); rit.hasNext();) {
682         String foundPackage = (String)rit.next();
683         boolean excludeThisPackage = false;
684
685         for (Iterator eit = option_exclude.iterator(); eit.hasNext();) {
686           String excludePackage = (String)eit.next();
687           if (foundPackage.equals(excludePackage) ||
688               foundPackage.startsWith(excludePackage + ":")) {
689             excludeThisPackage = true;
690             break;
691           }
692         }
693
694         if (!excludeThisPackage) {
695           rootDoc.addSpecifiedPackageName(foundPackage);
696           onePackageAdded = true;
697         }
698       }
699       if (!onePackageAdded) {
700         if (null != subpackage) {
701           reporter.printWarning("No non-excluded classes found under subpackage " + subpackage);
702         }
703         else {
704           reporter.printWarning("No non-excluded classes found.");
705         }
706       }
707     }
708   }
709
710   /**
711    *  Verify that the given file is a valid Java source file and that
712    *  it specifies the given package.
713    */
714   private boolean isValidJavaFile(File file,
715                                   String expectedPackage)
716   {
717     try {
718       InputStream in = new BufferedInputStream(new FileInputStream(file));
719
720       int ch, prevChar = 0;
721
722       final int STATE_DEFAULT = 0;
723       final int STATE_COMMENT = 1;
724       final int STATE_LINE_COMMENT = 2;
725
726       int state = STATE_DEFAULT;
727
728       StringBuffer word = new StringBuffer();
729       int wordIndex = 0;
730
731       while ((ch = in.read()) >= 0) {
732         String completeWord = null;
733
734         switch (state) {
735         case STATE_COMMENT:
736           if (prevChar == '*' && ch == '/') {
737             state = STATE_DEFAULT;
738           }
739           break;
740
741         case STATE_LINE_COMMENT:
742           if (ch == '\n') {
743             state = STATE_DEFAULT;
744           }
745           break;
746
747         case STATE_DEFAULT:
748           if (prevChar == '/' && ch == '*') {
749             word.deleteCharAt(word.length() - 1);
750             if (word.length() > 0) {
751               completeWord = word.toString();
752               word.setLength(0);
753             }
754             state = STATE_COMMENT;
755           }
756           else if (prevChar == '/' && ch == '/') {
757             word.deleteCharAt(word.length() - 1);
758             if (word.length() > 0) {
759               completeWord = word.toString();
760               word.setLength(0);
761             }
762             state = STATE_LINE_COMMENT;
763           }
764           else if (" \t\r\n".indexOf(ch) >= 0) {
765             if (word.length() > 0) {
766               completeWord = word.toString();
767               word.setLength(0);
768             }
769           }
770           else if (1 == wordIndex && ';' == ch) {
771             if (word.length() > 0) {
772               completeWord = word.toString();
773               word.setLength(0);
774             }
775             else {
776               // empty package name in source file: "package ;" -> invalid source file
777               in.close();
778               return false;
779             }
780           }
781           else {
782             word.append((char)ch);
783           }
784           break;
785         }
786
787         if (null != completeWord) {
788           if (0 == wordIndex && !"package".equals(completeWord)) {
789             in.close();
790             return "".equals(expectedPackage);
791           }
792           else if (1 == wordIndex) {
793             in.close();
794             return expectedPackage.equals(completeWord);
795           }
796           ++ wordIndex;
797         }
798
799         prevChar = ch;
800       }
801
802       // no package or class found before end-of-file -> invalid source file
803
804       in.close();
805       return false;
806     }
807     catch (IOException e) {
808       reporter.printWarning("Could not examine file " + file + ": " + e);
809       return false;
810     }
811   }
812
813   /**
814    *  Recursively try to locate valid Java packages under the given
815    *  package specified by its name and its directory. Add the names
816    *  of all valid packages to the result list.
817    */
818   private void findPackages(String subpackage,
819                             File packageDir,
820                             Set result)
821   {
822     File[] files = packageDir.listFiles();
823     if (null != files) {
824       for (int i=0; i<files.length; ++i) {
825         File file = files[i];
826         if (!file.isDirectory() && file.getName().endsWith(".java")) {
827           if (isValidJavaFile(file, subpackage)) {
828             if ("".equals(subpackage)) {
829               result.add(null);
830             }
831             else {
832               result.add(subpackage);
833             }
834             break;
835           }
836         }
837       }
838       for (int i=0; i<files.length; ++i) {
839         File file = files[i];
840         if (file.isDirectory()) {
841           String newSubpackage;
842           if (null != subpackage && subpackage.length() > 0) {
843             newSubpackage = subpackage + "." + file.getName();
844           }
845           else {
846             newSubpackage = file.getName();
847           }
848           findPackages(newSubpackage, file, result);
849         }
850       }
851     }
852   }
853
854   /**
855    *
856    */
857   private static boolean validOptions(String options[][],
858       DocErrorReporter reporter)
859   {
860
861     boolean foundDocletOption = false;
862     for (int i = 0; i < options.length; i++)
863     {
864       String[] opt = options[i];
865       if (opt[0].equalsIgnoreCase("-doclet"))
866       {
867         if (foundDocletOption)
868         {
869           reporter.printError("Only one -doclet option allowed.");
870           return false;
871         }
872         else
873         {
874           foundDocletOption = true;
875         }
876       }
877     }
878
879     return true;
880   }
881
882   /**
883    * Main entry point. This is the method called when gjdoc is invoked from the
884    * command line.
885    *
886    * @param args
887    *          command line arguments
888    */
889   public static void main(String[] args)
890   {
891
892     try
893     {
894       //--- Remember current time for profiling purposes
895
896       Timer.setStartTime();
897
898       //--- Handle control to the Singleton instance of this class
899
900       int result = instance.start(args);
901
902       if (result < 0) {
903         // fatal error
904         System.exit(5);
905       }
906       else if (result > 0) {
907         // errors encountered
908         System.exit(1);
909       }
910       else {
911         // success
912         System.exit(0);
913       }
914     }
915     catch (Exception e)
916     {
917       //--- unexpected error
918       e.printStackTrace();
919       System.exit(1);
920     }
921   }
922
923   /**
924    * Parses command line arguments and subsequentially handles control to the
925    * startDoclet() method
926    *
927    * @param args The command line parameters.
928    */
929    public static int execute(String[] args)
930    {
931      try
932      {
933        int result = instance.start(args);
934        if (result < 0) {
935          // fatal error
936          return 5;
937        }
938        else if (result > 0) {
939          // errors encountered
940          return 1;
941        }
942        else {
943          // success
944          return 0;
945        }
946      }
947      catch (Exception e)
948      {
949        // unexpected error
950        return 1;
951      }
952    }
953
954   /**
955    * @param programName Name of the program (for error messages). *disregarded*
956    * @param args The command line parameters.
957    * @returns The return code.
958    */
959   public static int execute(String programName,
960                             String[] args)
961   {
962     return execute(args);
963   }
964
965   /**
966    * @param programName Name of the program (for error messages).
967    * @param defaultDocletClassName Fully qualified class name.
968    * @param args The command line parameters.
969    * @returns The return code.
970    *//*
971   public static int execute(String programName,
972                             String defaultDocletClassName,
973                             String[] args)
974   {
975     // not yet implemented
976   }*/
977
978   /**
979    * @param programName Name of the program (for error messages).
980    * @param defaultDocletClassName Fully qualified class name.
981    * @param args The command line parameters.
982    * @returns The return code.
983    *//*
984   public static int execute(String programName,
985                             String defaultDocletClassName,
986                             String[] args)
987   {
988     // not yet implemented
989   }*/
990
991   /**
992    * @param programName Name of the program (for error messages).
993    * @param errWriter PrintWriter to receive error messages.
994    * @param warnWriter PrintWriter to receive error messages.
995    * @param noticeWriter PrintWriter to receive error messages.
996    * @param defaultDocletClassName Fully qualified class name.
997    * @param args The command line parameters.
998    * @returns The return code.
999    *//*
1000   public static int execute(String programName,
1001                             PrintWriter errWriter,
1002                             PrintWriter warnWriter,
1003                             PrintWriter noticeWriter,
1004                             String defaultDocletClassName,
1005                             String[] args)
1006   {
1007     // not yet implemented
1008   }*/
1009
1010   /**
1011    * Parses command line arguments and subsequentially handles control to the
1012    * startDoclet() method
1013    *
1014    * @param args
1015    *          Command line arguments, as passed to the main() method
1016    * @return {@code -1} in case of a fatal error (invalid arguments),
1017    * or the number of errors encountered.
1018    * @exception ParseException
1019    *              FIXME
1020    * @exception IOException
1021    *              if an IO problem occur
1022    */
1023   public int start(String[] args) throws ParseException, IOException
1024   {
1025
1026     //--- Collect unparsed arguments in array and resolve references
1027     //         to external argument files.
1028
1029     List arguments = new ArrayList(args.length);
1030
1031     for (int i = 0; i < args.length; ++i)
1032     {
1033       if (!args[i].startsWith("@"))
1034       {
1035         arguments.add(args[i]);
1036       }
1037       else
1038       {
1039         FileReader reader = new FileReader(args[i].substring(1));
1040         StreamTokenizer st = new StreamTokenizer(reader);
1041         st.resetSyntax();
1042         st.wordChars('\u0000', '\uffff');
1043         st.quoteChar('\"');
1044         st.quoteChar('\'');
1045         st.whitespaceChars(' ', ' ');
1046         st.whitespaceChars('\t', '\t');
1047         st.whitespaceChars('\r', '\r');
1048         st.whitespaceChars('\n', '\n');
1049         while (st.nextToken() != StreamTokenizer.TT_EOF)
1050         {
1051           arguments.add(st.sval);
1052         }
1053       }
1054     }
1055
1056     //--- Initialize Map for option parsing
1057
1058     initOptions();
1059
1060     //--- This will hold all options recognized by gjdoc itself
1061     //         and their associated arguments.
1062     //         Contains objects of type String[], where each entry
1063     //         specifies an option along with its aguments.
1064
1065     List options = new LinkedList();
1066
1067     //--- This will hold all command line tokens not recognized
1068     //         to be part of a standard option.
1069     //         These options are intended to be processed by the doclet
1070     //         Contains objects of type String, where each entry is
1071     //         one unrecognized token.
1072
1073     List customOptions = new LinkedList();
1074
1075     rootDoc = new RootDocImpl();
1076     reporter = rootDoc.getReporter();
1077
1078     //--- Iterate over all options given on the command line
1079
1080     for (Iterator it = arguments.iterator(); it.hasNext();)
1081     {
1082
1083       String arg = (String) it.next();
1084
1085       //--- Check if gjdoc recognizes this option as a standard option
1086       //         and remember the options' argument count
1087
1088       int optlen = optionLength(arg);
1089
1090       //--- Argument count == 0 indicates that the option is not recognized.
1091       //         Add it to the list of custom option tokens
1092
1093       //--- Otherwise the option is recognized as a standard option.
1094       //         if all required arguments are supplied. Create a new String
1095       //         array for the option and its arguments, and store it
1096       //         in the options array.
1097
1098       if (optlen > 0)
1099       {
1100         String[] option = new String[optlen];
1101         option[0] = arg;
1102         boolean optargs_ok = true;
1103         for (int j = 1; j < optlen && optargs_ok; ++j)
1104         {
1105           if (it.hasNext())
1106           {
1107             option[j] = (String) it.next();
1108             if (option[j].startsWith("-"))
1109             {
1110               optargs_ok = false;
1111             }
1112           }
1113           else
1114           {
1115             optargs_ok = false;
1116           }
1117         }
1118         if (optargs_ok)
1119           options.add(option);
1120         else
1121         {
1122           //         If the option requires more arguments than given on the
1123           //         command line, issue a fatal error
1124
1125           reporter.printFatal("Missing value for option " + arg + ".");
1126         }
1127       }
1128     }
1129
1130     //--- Create an array of String arrays from the dynamic array built above
1131
1132     String[][] optionArr = (String[][]) options.toArray(new String[options
1133         .size()][0]);
1134
1135     //--- Validate all options and issue warnings/errors
1136
1137     if (validOptions(optionArr, rootDoc))
1138     {
1139
1140       //--- We got valid options; parse them and store the parsed values
1141       //         in 'option_*' fields.
1142
1143       readOptions(optionArr);
1144
1145       //--- Show version and exit if requested by user
1146
1147       if (option_showVersion) {
1148         System.out.println("gjdoc " + getGjdocVersion());
1149         System.exit(0);
1150       }
1151
1152       if (option_bootclasspath_specified) {
1153         reporter.printWarning("-bootclasspath ignored: not supported by"
1154                               + " gjdoc wrapper script, or no wrapper script in use.");
1155       }
1156
1157       // If we have an empty source path list, add the current directory ('.')
1158
1159       if (option_sourcepath.size() == 0)
1160         option_sourcepath.add(new File("."));
1161
1162       //--- We have all information we need to start the doclet at this time
1163
1164       if (null != option_encoding) {
1165         rootDoc.setSourceEncoding(option_encoding);
1166       }
1167       else {
1168         // be quiet about this for now:
1169         // reporter.printNotice("No encoding specified, using platform default: " + System.getProperty("file.encoding"));
1170         rootDoc.setSourceEncoding(System.getProperty("file.encoding"));
1171       }
1172       rootDoc.setSourcePath(option_sourcepath);
1173
1174       //addJavaLangClasses();
1175
1176       if (!startDoclet(arguments)) {
1177         return -1;
1178       }
1179     }
1180
1181     return reporter.getErrorCount();
1182   }
1183
1184   private void addJavaLangClasses()
1185     throws IOException
1186   {
1187     String resourceName = "/java.lang-classes-" + option_source + ".txt";
1188     InputStream in = getClass().getResourceAsStream(resourceName);
1189     BufferedReader reader = new BufferedReader(new InputStreamReader(in));
1190     String line;
1191     while ((line = reader.readLine()) != null) {
1192
1193       String className = line.trim();
1194       if (className.length() > 0) {
1195         ClassDocImpl classDoc =
1196           new ClassDocImpl(null, new PackageDocImpl("java.lang"),
1197                            ProgramElementDocImpl.ACCESS_PUBLIC,
1198                            false, false, null);
1199         classDoc.setClass(className);
1200         rootDoc.addClassDoc(classDoc);
1201       }
1202     }
1203   }
1204
1205   /**
1206    * Helper class for parsing command line arguments. An instance of this class
1207    * represents a particular option accepted by gjdoc (e.g. '-sourcepath') along
1208    * with the number of expected arguments and behavior to parse the arguments.
1209    */
1210   private abstract class OptionProcessor
1211   {
1212
1213     /**
1214      * Number of arguments expected by this option.
1215      */
1216     private int argCount;
1217
1218     /**
1219      * Initializes this instance.
1220      *
1221      * @param argCount
1222      *          number of arguments
1223      */
1224     public OptionProcessor(int argCount)
1225     {
1226       this.argCount = argCount;
1227     }
1228
1229     /**
1230      * Overridden by derived classes with behavior to parse the arguments
1231      * specified with this option.
1232      *
1233      * @param args
1234      *          command line arguments
1235      */
1236     abstract void process(String[] args);
1237   }
1238
1239   /**
1240    * Maps option tags (e.g. '-sourcepath') to OptionProcessor objects.
1241    * Initialized only once by method initOptions(). FIXME: Rename to
1242    * 'optionProcessors'.
1243    */
1244   private static Map options = null;
1245
1246   /**
1247    * Initialize all OptionProcessor objects needed to scan/parse command line
1248    * options. This cannot be done in a static initializer block because
1249    * OptionProcessors need access to the Singleton instance of the Main class.
1250    */
1251   private void initOptions()
1252   {
1253
1254     options = new HashMap();
1255
1256     //--- Put one OptionProcessor object into the map
1257     //         for each option recognized.
1258
1259     options.put("-overview", new OptionProcessor(2)
1260       {
1261
1262         void process(String[] args)
1263         {
1264           option_overview = args[0];
1265         }
1266       });
1267     options.put("-public", new OptionProcessor(1)
1268       {
1269
1270         void process(String[] args)
1271         {
1272           option_coverage = COVERAGE_PUBLIC;
1273         }
1274       });
1275     options.put("-protected", new OptionProcessor(1)
1276       {
1277
1278         void process(String[] args)
1279         {
1280           option_coverage = COVERAGE_PROTECTED;
1281         }
1282       });
1283     options.put("-package", new OptionProcessor(1)
1284       {
1285
1286         void process(String[] args)
1287         {
1288           option_coverage = COVERAGE_PACKAGE;
1289         }
1290       });
1291     options.put("-private", new OptionProcessor(1)
1292       {
1293
1294         void process(String[] args)
1295         {
1296           option_coverage = COVERAGE_PRIVATE;
1297         }
1298       });
1299     OptionProcessor helpProcessor = new OptionProcessor(1)
1300       {
1301
1302         void process(String[] args)
1303         {
1304           option_help = true;
1305         }
1306       };
1307
1308     options.put("-help", helpProcessor);
1309     options.put("--help", helpProcessor);
1310     options.put("-doclet", new OptionProcessor(2)
1311         {
1312
1313           void process(String[] args)
1314           {
1315             option_doclet = args[0];
1316           }
1317         });
1318     options.put("-docletpath", new OptionProcessor(2)
1319         {
1320
1321           void process(String[] args)
1322           {
1323             option_docletpath = args[0];
1324           }
1325         });
1326     options.put("-nowarn", new OptionProcessor(1)
1327         {
1328
1329           void process(String[] args)
1330           {
1331             option_nowarn = true;
1332           }
1333         });
1334     options.put("-source", new OptionProcessor(2)
1335         {
1336
1337           void process(String[] args)
1338           {
1339             option_source = args[0];
1340             if ("1.5".equals(option_source)
1341                 || "1.6".equals(option_source)
1342                 || "1.7".equals(option_source)) {
1343               System.err.println("WARNING: support for option -source " + option_source + " is experimental");
1344             }
1345             else if (!"1.2".equals(option_source)
1346                 && !"1.3".equals(option_source)
1347                 && !"1.4".equals(option_source)) {
1348
1349               throw new RuntimeException("Only the following values are currently"
1350                                          + " supported for option -source: 1.2, 1.3, 1.4; experimental: 1.5, 1.6, 1.7.");
1351             }
1352           }
1353         });
1354     OptionProcessor sourcePathProcessor = new OptionProcessor(2) {
1355         void process(String[] args)
1356         {
1357           Debug.log(1, "-sourcepath is '" + args[0] + "'");
1358           for (StringTokenizer st = new StringTokenizer(args[0],
1359               File.pathSeparator); st.hasMoreTokens();)
1360           {
1361             String path = st.nextToken();
1362             File file = new File(path);
1363             if (!(file.exists()))
1364             {
1365               throw new RuntimeException("The source path " + path
1366                   + " does not exist.");
1367             }
1368             option_sourcepath.add(file);
1369           }
1370         }
1371       };
1372     options.put("-s", sourcePathProcessor);
1373     options.put("-sourcepath", sourcePathProcessor);
1374     options.put("-subpackages", new OptionProcessor(2)
1375       {
1376         void process(String[] args)
1377         {
1378           StringTokenizer st = new StringTokenizer(args[0], ":");
1379           while (st.hasMoreTokens()) {
1380             String packageName = st.nextToken();
1381
1382             if (packageName.startsWith(".")
1383                 || packageName.endsWith(".")
1384                 || packageName.indexOf("..") > 0
1385                 || !checkCharSet(packageName,
1386                                  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_.")) {
1387               throw new RuntimeException("Illegal package name '"
1388                                          + packageName + "'");
1389             }
1390             option_subpackages.add(packageName);
1391           }
1392         }
1393       });
1394     options.put("-exclude", new OptionProcessor(2)
1395       {
1396         void process(String[] args)
1397         {
1398           StringTokenizer st = new StringTokenizer(args[0], ":");
1399           while (st.hasMoreTokens()) {
1400             String packageName = st.nextToken();
1401
1402             if (packageName.startsWith(".")
1403                 || packageName.endsWith(".")
1404                 || packageName.indexOf("..") > 0
1405                 || !checkCharSet(packageName,
1406                                  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_.")) {
1407               throw new RuntimeException("Illegal package name '"
1408                                          + packageName + "'");
1409             }
1410             option_exclude.add(packageName);
1411           }
1412         }
1413       });
1414     // TODO include other options here
1415     options.put("-verbose", new OptionProcessor(1)
1416       {
1417
1418         void process(String[] args)
1419         {
1420           option_verbose = true;
1421           System.err.println("WARNING: Unsupported option -verbose ignored");
1422         }
1423       });
1424     options.put("-quiet", new OptionProcessor(1)
1425       {
1426
1427         void process(String[] args)
1428         {
1429           reporter.setQuiet(true);
1430         }
1431       });
1432     options.put("-locale", new OptionProcessor(2)
1433       {
1434
1435         void process(String[] args)
1436         {
1437           String localeName = args[0];
1438           String language = null;
1439           String country = null;
1440           String variant = null;
1441           StringTokenizer st = new StringTokenizer(localeName, "_");
1442           if (st.hasMoreTokens()) {
1443             language = st.nextToken();
1444           }
1445           if (st.hasMoreTokens()) {
1446             country = st.nextToken();
1447           }
1448           if (st.hasMoreTokens()) {
1449             variant = st.nextToken();
1450           }
1451           if (variant != null) {
1452             option_locale = new Locale(language, country, variant);
1453           }
1454           else if (country != null) {
1455              option_locale = new Locale(language, country);
1456           }
1457           else if (language != null) {
1458              option_locale = new Locale(language);
1459           }
1460           else {
1461               throw new RuntimeException("Illegal locale specification '"
1462                                          + localeName + "'");
1463           }
1464         }
1465       });
1466     options.put("-encoding", new OptionProcessor(2)
1467       {
1468
1469         void process(String[] args)
1470         {
1471           option_encoding = args[0];
1472         }
1473       });
1474     options.put("-breakiterator", new OptionProcessor(1)
1475       {
1476         void process(String[] args)
1477         {
1478           option_breakiterator = true;
1479         }
1480       });
1481     options.put("-licensetext", new OptionProcessor(1)
1482       {
1483         void process(String[] args)
1484         {
1485           option_licensetext = true;
1486         }
1487       });
1488     options.put("-overview", new OptionProcessor(2)
1489       {
1490         void process(String[] args)
1491         {
1492           try {
1493             getRootDoc().setRawCommentText(RootDocImpl.readHtmlBody(new File(args[0])));
1494           }
1495           catch (IOException e) {
1496             throw new RuntimeException("Cannot read file specified in option -overview: " + e.getMessage());
1497           }
1498         }
1499       });
1500     options.put("-classpath", new OptionProcessor(2)
1501       {
1502         void process(String[] args)
1503         {
1504           reporter.printWarning("-classpath option could not be passed to the VM.  Faking it with ");
1505           reporter.printWarning("    System.setProperty(\"java.class.path\", \"" + args[0] + "\");");
1506           System.setProperty("java.class.path", args[0]);
1507         }
1508       });
1509     options.put("--version", new OptionProcessor(1)
1510       {
1511         void process(String[] args)
1512         {
1513           option_showVersion = true;
1514         }
1515       });
1516     options.put("-bootclasspath", new OptionProcessor(1)
1517       {
1518         void process(String[] args)
1519         {
1520           option_bootclasspath_specified = true;
1521         }
1522       });
1523     options.put("-all", new OptionProcessor(1)
1524       {
1525         void process(String[] args)
1526         {
1527           option_all = true;
1528         }
1529       });
1530     options.put("-reflection", new OptionProcessor(1)
1531       {
1532         void process(String[] args)
1533         {
1534           option_reflection = true;
1535         }
1536       });
1537   }
1538
1539   /**
1540    * Determine how many arguments the given option requires.
1541    *
1542    * @param option
1543    *          The name of the option without leading dash.
1544    */
1545   private static int optionLength(String option)
1546   {
1547
1548     OptionProcessor op = (OptionProcessor) options.get(option.toLowerCase());
1549     if (op != null)
1550       return op.argCount;
1551     else
1552       return 0;
1553   }
1554
1555   /**
1556    * Process all given options. Assumes that the options have been validated
1557    * before.
1558    *
1559    * @param optionArr
1560    *          Each element is a series of Strings where [0] is the name of the
1561    *          option and [1..n] are the arguments to the option.
1562    */
1563   private void readOptions(String[][] optionArr)
1564   {
1565
1566     //--- For each option, find the appropriate OptionProcessor
1567     //        and call its process() method
1568
1569     for (int i = 0; i < optionArr.length; ++i)
1570     {
1571       String[] opt = optionArr[i];
1572       String[] args = new String[opt.length - 1];
1573       System.arraycopy(opt, 1, args, 0, opt.length - 1);
1574       OptionProcessor op = (OptionProcessor) options.get(opt[0].toLowerCase());
1575       op.process(args);
1576     }
1577   }
1578
1579   /**
1580    * Print command line usage.
1581    */
1582   private static void usage()
1583   {
1584     System.out
1585         .print("\n"
1586             + "USAGE: gjdoc [options] [packagenames] "
1587             + "[sourcefiles] [@files]\n\n"
1588             + "  --version                Show version information and exit\n"
1589             + "  -all                     Process all source files found in the source path\n"
1590             + "  -overview <file>         Read overview documentation from HTML file\n"
1591             + "  -public                  Include only public classes and members\n"
1592             + "  -protected               Include protected and public classes and members\n"
1593             + "                           This is the default\n"
1594             + "  -package                 Include package/protected/public classes and members\n"
1595             + "  -private                 Include all classes and members\n"
1596             + "  -help, --help            Show this information\n"
1597             + "  -doclet <class>          Doclet class to use for generating output\n"
1598             + "  -docletpath <classpath>  Specifies the search path for the doclet and\n"
1599             + "                           dependencies\n"
1600             + "  -source <release>        Provide source compatibility with specified\n"
1601             + "                           release (1.4 to handle assertion)\n"
1602             + "  -sourcepath <pathlist>   Where to look for source files\n"
1603             + "  -s <pathlist>            Alias for -sourcepath\n"
1604             + "  -subpackages <spkglist>  List of subpackages to recursively load\n"
1605             + "  -exclude <pkglist>       List of packages to exclude\n"
1606             + "  -verbose                 Output messages about what Gjdoc is doing [ignored]\n"
1607             + "  -quiet                   Do not print non-error and non-warning messages\n"
1608             + "  -locale <name>           Locale to be used, e.g. en_US or en_US_WIN\n"
1609             + "  -encoding <name>         Source file encoding name\n"
1610             + "  -breakiterator           Compute first sentence with BreakIterator\n"
1611             + "  -classpath <pathlist>    Set the path used for loading auxilliary classes\n"
1612             + "\n"
1613             + "Standard doclet options:\n"
1614             + "  -d                      Set target directory\n"
1615             + "  -use                    Includes the 'Use' page for each documented class\n"
1616             + "                          and package\n"
1617             + "  -version                Includes the '@version' tag\n"
1618             + "  -author                 Includes the '@author' tag\n"
1619             + "  -splitindex             Splits the index file into multiple files\n"
1620             + "  -windowtitle <text>     Browser window title\n"
1621             + "  -doctitle <text>        Title near the top of the overview summary file\n"
1622             + "                          (HTML allowed)\n"
1623             + "  -title <text>           Title for this set of API documentation\n"
1624             + "                          (deprecated, -doctitle should be used instead)\n"
1625             + "  -header <text>          Text to include in the top navigation bar\n"
1626             + "                          (HTML allowed)\n"
1627             + "  -footer <text>          Text to include in the bottom navigation bar\n"
1628             + "                          (HTML allowed)\n"
1629             + "  -bottom <text>          Text to include at the bottom of each output file\n"
1630             + "                          (HTML allowed)\n"
1631             + "  -link <extdoc URL>      Link to external generated documentation at URL\n"
1632             + "  -linkoffline <extdoc URL> <packagelistLoc>\n"
1633             + "                          Link to external generated documentation for\n"
1634             + "                          the specified package-list\n"
1635             + "  -linksource             Creates an HTML version of each source file\n"
1636             + "  -group <groupheading> <packagepattern:packagepattern:...>\n"
1637             + "                          Separates packages on the overview page into groups\n"
1638             + "  -nodeprecated           Prevents the generation of any deprecated API\n"
1639             + "  -nodeprecatedlist       Prevents the generation of the file containing\n"
1640             + "                          the list of deprecated APIs and the link to the\n"
1641             + "                          navigation bar to that page\n"
1642             + "  -nosince                Omit the '@since' tag\n"
1643             + "  -notree                 Do not generate the class/interface hierarchy page\n"
1644             + "  -noindex                Do not generate the index file\n"
1645             + "  -nohelp                 Do not generate the help link\n"
1646             + "  -nonavbar               Do not generate the navbar, header and footer\n"
1647             + "  -helpfile <filen>       Path to an alternate help file\n"
1648             + "  -stylesheetfile <file>  Path to an alternate CSS stylesheet\n"
1649             + "  -addstylesheet <file>   Path to an additional CSS stylesheet\n"
1650             + "  -serialwarn             Complain about missing '@serial' tags [ignored]\n"
1651             + "  -charset <IANACharset>  Specifies the HTML charset\n"
1652             + "  -docencoding <IANACharset>\n"
1653             + "                          Specifies the encoding of the generated HTML files\n"
1654             + "  -tag <tagname>:Xaoptcmf:\"<taghead>\"\n"
1655             + "                          Enables gjdoc to interpret a custom tag\n"
1656             + "  -taglet                 Adds a Taglet class to the map of taglets\n"
1657             + "  -tagletpath             Sets the CLASSPATH to load subsequent Taglets from\n"
1658             + "  -docfilessubdirs        Enables deep copy of 'doc-files' directories\n"
1659             + "  -excludedocfilessubdir <name1:name2:...>\n"
1660             + "                          Excludes 'doc-files' subdirectories with a give name\n"
1661             + "  -noqualifier all|<packagename1:packagename2:...>\n"
1662             + "                          Do never fully qualify given package names\n"
1663             + "  -nocomment              Suppress the entire comment body including the main\n"
1664             + "                          description and all tags, only generate declarations\n"
1665             + "\n"
1666             + "Gjdoc extension options:\n"
1667             + "  -reflection             Use reflection for resolving unqualified class names\n"
1668             + "  -licensetext            Include license text from source files\n"
1669             + "  -validhtml              Use valid HTML/XML names (breaks compatibility)\n"
1670             + "  -baseurl <url>          Hardwire the given base URL into generated pages\n"
1671                /**
1672             + "  -genhtml                Generate HTML code instead of XML code. This is the\n"
1673             + "                          default.\n"
1674             + "  -geninfo                Generate Info code instead of XML code.\n"
1675             + "  -xslsheet <file>        If specified, XML files will be written to a\n"
1676             + "                          temporary directory and transformed using the\n"
1677             + "                          given XSL sheet. The result of the transformation\n"
1678             + "                          is written to the output directory. Not required if\n"
1679             + "                          -genhtml or -geninfo has been specified.\n"
1680             + "  -xmlonly                Generate XML code only, do not generate HTML code.\n"
1681             + "  -bottomnote             HTML code to include at the bottom of each page.\n"
1682             + "  -nofixhtml              If not specified, heurestics will be applied to\n"
1683             + "                          fix broken HTML code in comments.\n"
1684             + "  -nohtmlwarn             Do not emit warnings when encountering broken HTML\n"
1685             + "                          code.\n"
1686             + "  -noemailwarn            Do not emit warnings when encountering strings like\n"
1687             + "                          <abc@foo.com>.\n"
1688             + "  -indentstep <n>         How many spaces to indent each tag level in\n"
1689             + "                          generated XML code.\n"
1690             + "  -xsltdriver <class>     Specifies the XSLT driver to use for transformation.\n"
1691             + "                          By default, xsltproc is used.\n"
1692             + "  -postprocess <class>    XmlDoclet postprocessor class to apply after XSL\n"
1693             + "                          transformation.\n"
1694             + "  -compress               Generated info pages will be Zip-compressed.\n"
1695             + "  -workpath               Specify a temporary directory to use.\n"
1696             + "  -authormail <type>      Specify handling of mail addresses in @author tags.\n"
1697             + "     no-replace             do not replace mail addresses (default).\n"
1698             + "     mailto-name            replace by <a>Real Name</a>.\n"
1699             + "     name-mailto-address    replace by Real Name (<a>abc@foo.com</a>).\n"
1700             + "     name-mangled-address   replace by Real Name (<a>abc AT foo DOT com</a>).\n"
1701                **/
1702             );
1703   }
1704
1705   /**
1706    * The root of the gjdoc tool.
1707    *
1708    * @return all the options of the gjdoc application.
1709    */
1710   public static RootDocImpl getRootDoc()
1711   {
1712     return rootDoc;
1713   }
1714
1715   /**
1716    * Get the gjdoc singleton.
1717    *
1718    * @return the gjdoc instance.
1719    */
1720   public static Main getInstance()
1721   {
1722     return instance;
1723   }
1724
1725   /**
1726    * Is this access level covered?
1727    *
1728    * @param accessLevel
1729    *          the access level we want to know if it is covered.
1730    * @return true if the access level is covered.
1731    */
1732   public boolean includeAccessLevel(int accessLevel)
1733   {
1734     return coverageTemplates[option_coverage][accessLevel];
1735   }
1736
1737   /**
1738    * Is the doclet running?
1739    *
1740    * @return true if it's running
1741    */
1742   public boolean isDocletRunning()
1743   {
1744     return docletRunning;
1745   }
1746
1747   /**
1748    * Check the charset. Check that all the characters of the string 'toCheck'
1749    * and query if they exist in the 'charSet'. The order does not matter. The
1750    * number of times a character is in the variable does not matter.
1751    *
1752    * @param toCheck
1753    *          the charset to check.
1754    * @param charSet
1755    *          the reference charset
1756    * @return true if they match.
1757    */
1758   public static boolean checkCharSet(String toCheck, String charSet)
1759   {
1760     for (int i = 0; i < toCheck.length(); ++i)
1761     {
1762       if (charSet.indexOf(toCheck.charAt(i)) < 0)
1763         return false;
1764     }
1765     return true;
1766   }
1767
1768   /**
1769    * Makes the RootDoc eligible for the GC.
1770    */
1771   public static void releaseRootDoc()
1772   {
1773     rootDoc.flush();
1774   }
1775
1776   /**
1777    * Return whether the -breakiterator option has been specified.
1778    */
1779   public boolean isUseBreakIterator()
1780   {
1781     return this.option_breakiterator
1782       || !getLocale().getLanguage().equals(Locale.ENGLISH.getLanguage());
1783   }
1784
1785   /**
1786    * Return whether boilerplate license text should be copied.
1787    */
1788   public boolean isCopyLicenseText()
1789   {
1790     return this.option_licensetext;
1791   }
1792
1793   /**
1794    *  Return the locale specified using the -locale option or the
1795    *  default locale;
1796    */
1797   public Locale getLocale()
1798   {
1799     return this.option_locale;
1800   }
1801
1802   /**
1803    *  Return the collator to use based on the specified -locale
1804    *  option. If no collator can be found for the given locale, a
1805    *  warning is emitted and the default collator is used instead.
1806    */
1807   public Collator getCollator()
1808   {
1809     if (null == this.collator) {
1810       Locale locale = getLocale();
1811       this.collator = Collator.getInstance(locale);
1812       Locale defaultLocale = Locale.getDefault();
1813       if (null == this.collator
1814           && !defaultLocale.equals(locale)) {
1815         this.collator = Collator.getInstance(defaultLocale);
1816         if (null != this.collator) {
1817           reporter.printWarning("No collator found for locale "
1818                                 + locale.getDisplayName()
1819                                 + "; using collator for default locale "
1820                                 + defaultLocale.getDisplayName()
1821                                 + ".");
1822         }
1823         else {
1824           this.collator = Collator.getInstance();
1825           reporter.printWarning("No collator found for specified locale "
1826                                 + locale.getDisplayName()
1827                                 + " or default locale "
1828                                 + defaultLocale.getDisplayName()
1829                                 + ": using default collator.");
1830         }
1831       }
1832       if (null == this.collator) {
1833         this.collator = Collator.getInstance();
1834         reporter.printWarning("No collator found for locale "
1835                               + locale.getDisplayName()
1836                               + ": using default collator.");
1837       }
1838     }
1839     return this.collator;
1840   }
1841
1842   public boolean isCacheRawComments()
1843   {
1844     return true;
1845   }
1846
1847   public String getGjdocVersion()
1848   {
1849     if (null == gjdocVersion) {
1850       gjdocVersion = gnu.classpath.Configuration.CLASSPATH_VERSION;
1851     }
1852     return gjdocVersion;
1853   }
1854
1855   public boolean isReflectionEnabled()
1856   {
1857     return this.option_reflection;
1858   }
1859 }