1package jdiff;
2
3import java.io.*;
4import java.util.*;
5import com.sun.javadoc.*;
6
7/**
8 * Class to handle options for JDiff.
9 *
10 * See the file LICENSE.txt for copyright details.
11 * @author Matthew Doar, mdoar@pobox.com
12 */
13public class Options {
14
15    /** Default constructor. */
16    public Options() {
17    }
18
19    /**
20     * Returns the "length" of a given option. If an option takes no
21     * arguments, its length is one. If it takes one argument, its
22     * length is two, and so on. This method is called by Javadoc to
23     * parse the options it does not recognize. It then calls
24     * {@link #validOptions} to validate them.
25     * <blockquote>
26     * <b>Note:</b><br>
27     * The options arrive as case-sensitive strings. For options that
28     * are not case-sensitive, use toLowerCase() on the option string
29     * before comparing it.
30     * </blockquote>
31     *
32     * @param option  a String containing an option
33     * @return an int telling how many components that option has
34     */
35    public static int optionLength(String option) {
36        String opt = option.toLowerCase();
37
38        // Standard options
39        if (opt.equals("-authorid"))  return 2;
40        if (opt.equals("-versionid")) return 2;
41        if (opt.equals("-d"))         return 2;
42        if (opt.equals("-classlist")) return 1;
43        if (opt.equals("-title"))     return 2;
44        if (opt.equals("-docletid"))  return 1;
45        if (opt.equals("-evident"))   return 2;
46        if (opt.equals("-skippkg"))   return 2;
47        if (opt.equals("-skipclass")) return 2;
48        if (opt.equals("-execdepth")) return 2;
49        if (opt.equals("-help"))      return 1;
50        if (opt.equals("-version"))      return 1;
51        if (opt.equals("-package"))   return 1;
52        if (opt.equals("-protected")) return 1;
53        if (opt.equals("-public"))    return 1;
54        if (opt.equals("-private"))   return 1;
55        if (opt.equals("-sourcepath")) return 2;
56
57        // Options to control JDiff
58        if (opt.equals("-apiname"))    return 2;
59        if (opt.equals("-oldapi"))    return 2;
60        if (opt.equals("-newapi"))    return 2;
61
62        // Options to control the location of the XML files
63        if (opt.equals("-apidir"))       return 2;
64        if (opt.equals("-oldapidir"))    return 2;
65        if (opt.equals("-newapidir"))    return 2;
66        if (opt.equals("-usercommentsdir"))    return 2;
67
68
69        // Options for the exclusion level for classes and members
70        if (opt.equals("-excludeclass"))    return 2;
71        if (opt.equals("-excludemember"))    return 2;
72
73        if (opt.equals("-firstsentence"))    return 1;
74        if (opt.equals("-docchanges"))    return 1;
75        if (opt.equals("-packagesonly"))    return 1;
76        if (opt.equals("-showallchanges"))    return 1;
77
78        // Option to change the location for the existing Javadoc
79        // documentation for the new API. Default is "../"
80        if (opt.equals("-javadocnew"))    return 2;
81        // Option to change the location for the existing Javadoc
82        // documentation for the old API. Default is null.
83        if (opt.equals("-javadocold"))    return 2;
84
85        if (opt.equals("-baseuri"))    return 2;
86
87        // Option not to suggest comments at all
88        if (opt.equals("-nosuggest"))    return 2;
89
90        // Option to enable checking that the comments end with a period.
91        if (opt.equals("-checkcomments"))    return 1;
92        // Option to retain non-printing characters in comments.
93        if (opt.equals("-retainnonprinting"))    return 1;
94        // Option for the name of the exclude tag
95        if (opt.equals("-excludetag"))    return 2;
96        // Generate statistical output
97        if (opt.equals("-stats"))    return 1;
98
99        // Set the browser window title
100        if (opt.equals("-windowtitle"))    return 2;
101        // Set the report title
102        if (opt.equals("-doctitle"))    return 2;
103
104        return 0;
105    }//optionLength()
106
107   /**
108    * After parsing the available options using {@link #optionLength},
109    * Javadoc invokes this method with an array of options-arrays, where
110    * the first item in any array is the option, and subsequent items in
111    * that array are its arguments. So, if -print is an option that takes
112    * no arguments, and -copies is an option that takes 1 argument, then
113    * <pre>
114    *     -print -copies 3
115    * </pre>
116    * produces an array of arrays that looks like:
117    * <pre>
118    *      option[0][0] = -print
119    *      option[1][0] = -copies
120    *      option[1][1] = 3
121    * </pre>
122    * (By convention, command line switches start with a "-", but
123    * they don't have to.)
124    * <p>
125    * <b>Note:</b><br>
126    * Javadoc passes <i>all</i>parameters to this method, not just
127    * those that Javadoc doesn't recognize. The only way to
128    * identify unexpected arguments is therefore to check for every
129    * Javadoc parameter as well as doclet parameters.
130    *
131    * @param options   an array of String arrays, one per option
132    * @param reporter  a DocErrorReporter for generating error messages
133    * @return true if no errors were found, and all options are
134    *         valid
135    */
136    public static boolean validOptions(String[][] options,
137                                       DocErrorReporter reporter) {
138        final DocErrorReporter errOut = reporter;
139
140        // A nice object-oriented way of handling errors. An instance of this
141        // class puts out an error message and keeps track of whether or not
142        // an error was found.
143        class ErrorHandler {
144            boolean noErrorsFound = true;
145            void msg(String msg) {
146                noErrorsFound = false;
147                errOut.printError(msg);
148            }
149        }
150
151        ErrorHandler err = new ErrorHandler();
152        if (trace)
153            System.out.println("Command line arguments: ");
154        for (int i = 0; i < options.length; i++) {
155            for (int j = 0; j < options[i].length; j++) {
156                Options.cmdOptions += " " + options[i][j];
157                if (trace)
158                    System.out.print(" " + options[i][j]);
159            }
160        }
161        if (trace)
162            System.out.println();
163
164        for (int i = 0; i < options.length; i++) {
165            if (options[i][0].toLowerCase().equals("-apiname")) {
166                if (options[i].length < 2) {
167                    err.msg("No version identifier specified after -apiname option.");
168                } else if (JDiff.compareAPIs) {
169                    err.msg("Use the -apiname option, or the -oldapi and -newapi options, but not both.");
170                } else {
171                    String filename = options[i][1];
172                    RootDocToXML.apiIdentifier = filename;
173                    filename = filename.replace(' ', '_');
174                    RootDocToXML.outputFileName =  filename + ".xml";
175                    JDiff.writeXML = true;
176                    JDiff.compareAPIs = false;
177                }
178                continue;
179            }
180            if (options[i][0].toLowerCase().equals("-apidir")) {
181                if (options[i].length < 2) {
182                    err.msg("No directory specified after -apidir option.");
183                } else {
184		    RootDocToXML.outputDirectory = options[i][1];
185                }
186                continue;
187            }
188	    if (options[i][0].toLowerCase().equals("-oldapi")) {
189                if (options[i].length < 2) {
190                    err.msg("No version identifier specified after -oldapi option.");
191                } else if (JDiff.writeXML) {
192                    err.msg("Use the -apiname or -oldapi option, but not both.");
193                } else {
194                    String filename = options[i][1];
195                    filename = filename.replace(' ', '_');
196                    JDiff.oldFileName =  filename + ".xml";
197                    JDiff.writeXML = false;
198                    JDiff.compareAPIs = true;
199                }
200                continue;
201            }
202            if (options[i][0].toLowerCase().equals("-oldapidir")) {
203                if (options[i].length < 2) {
204                    err.msg("No directory specified after -oldapidir option.");
205                } else {
206                	JDiff.oldDirectory = options[i][1];
207                }
208                continue;
209            }
210            if (options[i][0].toLowerCase().equals("-newapi")) {
211                if (options[i].length < 2) {
212                    err.msg("No version identifier specified after -newapi option.");
213                } else if (JDiff.writeXML) {
214                    err.msg("Use the -apiname or -newapi option, but not both.");
215                } else {
216                    String filename = options[i][1];
217                    filename = filename.replace(' ', '_');
218                    JDiff.newFileName =  filename + ".xml";
219                    JDiff.writeXML = false;
220                    JDiff.compareAPIs = true;
221                }
222                continue;
223            }
224            if (options[i][0].toLowerCase().equals("-newapidir")) {
225                if (options[i].length < 2) {
226                    err.msg("No directory specified after -newapidir option.");
227                } else {
228                	JDiff.newDirectory = options[i][1];
229                }
230                continue;
231            }
232            if (options[i][0].toLowerCase().equals("-usercommentsdir")) {
233                if (options[i].length < 2) {
234                    err.msg("Android: No directory specified after -usercommentsdir option.");
235                } else {
236                    HTMLReportGenerator.commentsDir = options[i][1];
237                }
238                continue;
239            }
240            if (options[i][0].toLowerCase().equals("-d")) {
241                if (options[i].length < 2) {
242                    err.msg("No directory specified after -d option.");
243                } else {
244                    HTMLReportGenerator.outputDir = options[i][1];
245                }
246                continue;
247            }
248            if (options[i][0].toLowerCase().equals("-javadocnew")) {
249                if (options[i].length < 2) {
250                    err.msg("No location specified after -javadocnew option.");
251                } else {
252                    HTMLReportGenerator.newDocPrefix = options[i][1];
253                }
254                continue;
255            }
256            if (options[i][0].toLowerCase().equals("-javadocold")) {
257                if (options[i].length < 2) {
258                    err.msg("No location specified after -javadocold option.");
259                } else {
260                    HTMLReportGenerator.oldDocPrefix = options[i][1];
261                }
262                continue;
263            }
264            if (options[i][0].toLowerCase().equals("-baseuri")) {
265                if (options[i].length < 2) {
266                    err.msg("No base location specified after -baseURI option.");
267                } else {
268                    RootDocToXML.baseURI = options[i][1];
269                }
270                continue;
271            }
272            if (options[i][0].toLowerCase().equals("-excludeclass")) {
273                if (options[i].length < 2) {
274                    err.msg("No level (public|protected|package|private) specified after -excludeclass option.");
275                } else {
276                    String level = options[i][1];
277                    if (level.compareTo("public") != 0 &&
278                        level.compareTo("protected") != 0 &&
279                        level.compareTo("package") != 0 &&
280                        level.compareTo("private") != 0) {
281                        err.msg("Level specified after -excludeclass option must be one of (public|protected|package|private).");
282                    } else {
283                        RootDocToXML.classVisibilityLevel = level;
284                    }
285                }
286                continue;
287            }
288            if (options[i][0].toLowerCase().equals("-excludemember")) {
289                if (options[i].length < 2) {
290                    err.msg("No level (public|protected|package|private) specified after -excludemember option.");
291                } else {
292                    String level = options[i][1];
293                    if (level.compareTo("public") != 0 &&
294                        level.compareTo("protected") != 0 &&
295                        level.compareTo("package") != 0 &&
296                        level.compareTo("private") != 0) {
297                        err.msg("Level specified after -excludemember option must be one of (public|protected|package|private).");
298                    } else {
299                        RootDocToXML.memberVisibilityLevel = level;
300                    }
301                }
302                continue;
303            }
304            if (options[i][0].toLowerCase().equals("-firstsentence")) {
305                RootDocToXML.saveAllDocs = false;
306                continue;
307            }
308            if (options[i][0].toLowerCase().equals("-docchanges")) {
309                HTMLReportGenerator.reportDocChanges = true;
310                Diff.noDocDiffs = false;
311                continue;
312            }
313            if (options[i][0].toLowerCase().equals("-packagesonly")) {
314                RootDocToXML.packagesOnly = true;
315                continue;
316            }
317            if (options[i][0].toLowerCase().equals("-showallchanges")) {
318                Diff.showAllChanges = true;
319                continue;
320            }
321            if (options[i][0].toLowerCase().equals("-nosuggest")) {
322                if (options[i].length < 2) {
323                    err.msg("No level (all|remove|add|change) specified after -nosuggest option.");
324                } else {
325                    String level = options[i][1];
326                    if (level.compareTo("all") != 0 &&
327                        level.compareTo("remove") != 0 &&
328                        level.compareTo("add") != 0 &&
329                        level.compareTo("change") != 0) {
330                        err.msg("Level specified after -nosuggest option must be one of (all|remove|add|change).");
331                    } else {
332                        if (level.compareTo("removal") == 0)
333                            HTMLReportGenerator.noCommentsOnRemovals = true;
334                        else if (level.compareTo("add") == 0)
335                            HTMLReportGenerator.noCommentsOnAdditions = true;
336                        else if (level.compareTo("change") == 0)
337                            HTMLReportGenerator.noCommentsOnChanges = true;
338                        else if (level.compareTo("all") == 0) {
339                            HTMLReportGenerator.noCommentsOnRemovals = true;
340                            HTMLReportGenerator.noCommentsOnAdditions = true;
341                            HTMLReportGenerator.noCommentsOnChanges = true;
342                        }
343                    }
344                }
345                continue;
346            }
347            if (options[i][0].toLowerCase().equals("-checkcomments")) {
348                APIHandler.checkIsSentence = true;
349                continue;
350            }
351            if (options[i][0].toLowerCase().equals("-retainnonprinting")) {
352                RootDocToXML.stripNonPrintables = false;
353                continue;
354            }
355            if (options[i][0].toLowerCase().equals("-excludetag")) {
356                if (options[i].length < 2) {
357                    err.msg("No exclude tag specified after -excludetag option.");
358                } else {
359                    RootDocToXML.excludeTag = options[i][1];
360                    RootDocToXML.excludeTag = RootDocToXML.excludeTag.trim();
361                    RootDocToXML.doExclude = true;
362                }
363                continue;
364            }
365            if (options[i][0].toLowerCase().equals("-stats")) {
366                HTMLReportGenerator.doStats = true;
367                continue;
368            }
369            if (options[i][0].toLowerCase().equals("-doctitle")) {
370                if (options[i].length < 2) {
371                    err.msg("No HTML text specified after -doctitle option.");
372                } else {
373                    HTMLReportGenerator.docTitle = options[i][1];
374                }
375                continue;
376            }
377            if (options[i][0].toLowerCase().equals("-windowtitle")) {
378                if (options[i].length < 2) {
379                    err.msg("No text specified after -windowtitle option.");
380                } else {
381                    HTMLReportGenerator.windowTitle = options[i][1];
382                }
383                continue;
384            }
385            if (options[i][0].toLowerCase().equals("-version")) {
386                System.out.println("JDiff version: " + JDiff.version);
387                System.exit(0);
388            }
389            if (options[i][0].toLowerCase().equals("-help")) {
390                usage();
391                System.exit(0);
392            }
393        }//for
394        if (!JDiff.writeXML && !JDiff.compareAPIs) {
395            err.msg("First use the -apiname option to generate an XML file for one API.");
396            err.msg("Then use the -apiname option again to generate another XML file for a different version of the API.");
397            err.msg("Finally use the -oldapi option and -newapi option to generate a report about how the APIs differ.");
398        }
399        return err.noErrorsFound;
400    }// validOptions()
401
402    /** Display the arguments for JDiff. */
403    public static void usage() {
404        System.err.println("JDiff version: " + JDiff.version);
405        System.err.println("");
406        System.err.println("Valid JDiff arguments:");
407        System.err.println("");
408        System.err.println("  -apiname <Name of a version>");
409        System.err.println("  -oldapi <Name of a version>");
410        System.err.println("  -newapi <Name of a version>");
411
412        System.err.println("  Optional Arguments");
413        System.err.println();
414        System.err.println("  -d <directory> Destination directory for output HTML files");
415        System.err.println("  -oldapidir <directory> Location of the XML file for the old API");
416        System.err.println("  -newapidir <directory> Location of the XML file for the new API");
417        System.err.println("  -sourcepath <location of Java source files>");
418        System.err.println("  -javadocnew <location of existing Javadoc files for the new API>");
419        System.err.println("  -javadocold <location of existing Javadoc files for the old API>");
420        System.err.println("  -usercommentsdir <directory> Path to dir containing the user_comments* file(s)");
421
422        System.err.println("  -baseURI <base> Use \"base\" as the base location of the various DTDs and Schemas used by JDiff");
423        System.err.println("  -excludeclass [public|protected|package|private] Exclude classes which are not public, protected etc");
424        System.err.println("  -excludemember [public|protected|package|private] Exclude members which are not public, protected etc");
425
426        System.err.println("  -firstsentence Save only the first sentence of each comment block with the API.");
427        System.err.println("  -docchanges Report changes in Javadoc comments between the APIs");
428        System.err.println("  -nosuggest [all|remove|add|change] Do not add suggested comments to all, or the removed, added or chabged sections");
429        System.err.println("  -checkcomments Check that comments are sentences");
430        System.err.println("  -stripnonprinting Remove non-printable characters from comments.");
431        System.err.println("  -excludetag <tag> Define the Javadoc tag which implies exclusion");
432        System.err.println("  -stats Generate statistical output");
433        System.err.println("  -help       (generates this output)");
434        System.err.println("");
435        System.err.println("For more help, see jdiff.html");
436    }
437
438    /** All the options passed on the command line. Logged to XML. */
439    public static String cmdOptions = "";
440
441    /** Set to enable increased logging verbosity for debugging. */
442    private static boolean trace = false;
443}
444