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