1 package jdiff;
2 
3 import java.io.*;
4 import java.util.*;
5 import 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  */
13 public class Options {
14 
15     /** Default constructor. */
Options()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      */
optionLength(String option)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     */
validOptions(String[][] options, DocErrorReporter reporter)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. */
usage()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