001 package javafe; 002 003 import java.io.*; 004 import java.util.ArrayList; 005 import java.util.StringTokenizer; 006 007 import javafe.util.ErrorSet; 008 import javafe.util.UsageError; 009 import junitutils.Utils; 010 011 /** 012 * This is the super-class of classes that hold the values of 013 * command-line options. The options are separated from the Main 014 * class to improve modularity and to allow the options to be reset 015 * during a single execution of the main program. Consequently, none 016 * of the options fields should be static. The program may hold a 017 * static instance of an Options object if it wishes. 018 * 019 * <p> Derived classes should define overriding methods for 020 * <ul> 021 * <li> {@link #processOption(String, String[], int)}, 022 * <li> {@link #showNonOptions()}, 023 * <li> {@link #showOptions(boolean)} 024 * </ul> 025 */ 026 027 public class Options 028 { 029 /** 030 * Holds all the non-option arguments. 031 */ 032 public ArrayList inputEntries; // elements are InputEntry 033 034 /** 035 * Option to restrict output to error/caution/warning messages 036 * only - no progress or informational output. 037 */ 038 public boolean quiet = false; 039 040 /** 041 * Option to generate lots of output indicating what is happening 042 * during execution. 043 */ 044 public boolean v = false; 045 046 /** 047 * When true, no variable output (e.g., execution time) is 048 * printed, so that output can be compared to an oracle output 049 * file. Also, emit all paths for warnings, errors, etc. in 050 * canonical, machine-independent form. Such output is strictly 051 * used for unit testing. The canonical form of a path replaces 052 * all use of the slash ('/') and wack ('\') characters with bar 053 * ('|'). 054 */ 055 public boolean testMode = false; 056 057 /** 058 * Option to turn off caution warnings. This is used for Houdini 059 * where it is a pain to weed out the cautions from code where we 060 * are looking only at warnings. 061 */ 062 public boolean noCautions = false; 063 064 /** Option holding the current working directory. */ 065 public String currentdir = System.getProperty("user.dir"); 066 067 /** 068 * Option holding the user-specified classpath. 069 */ 070 public String userPath = null; 071 072 /** 073 * Option holding the user-specified sourcepath. 074 */ 075 public String userSourcePath = null; 076 077 /** 078 * Option holding the user-specified boot classpath. 079 */ 080 public String sysPath = null; 081 082 /** True if we should simply issue a usage message and abort. */ 083 public boolean issueUsage = false; 084 085 // Note - the "-v" option is directly set in javafe.util.Info.on 086 087 /** 088 * Are we parsing Java 1.4 source code (i.e., we must parse the 089 * new "assert" Java keyword). 090 * 091 * @design As Java evolves we'll likely have to change this to an 092 * enumeration type. 093 */ 094 public boolean assertIsKeyword = false; 095 096 /** 097 * Java allows assertions to be enabled and disabled. Replicate 098 * those options as well. 099 */ 100 public boolean assertionsEnabled = false; 101 102 /** 103 * Debugging flag used to turn on stack trace dumps when error 104 * messages are issued. (cf. javafe.util.ErrorSet) 105 */ 106 public boolean showErrorLocation = false; 107 108 /** 109 * Flags to use or not use source or binary files. 110 */ 111 static public final int PREFER_BINARY = 0; 112 static public final int PREFER_SOURCE = 1; 113 static public final int PREFER_RECENT = 2; 114 static public final int NEVER_BINARY = 3; 115 static public final int NEVER_SOURCE = 4; 116 public int fileOrigin = PREFER_RECENT; 117 118 public Options() { 119 // Everything should be initialized to default values. 120 javafe.util.Info.on = false; 121 } 122 123 /** 124 * Process tool options contained in <code>args</code>. 125 * 126 * @param args the command-line arguments that are being processed. 127 * @exception UsageError If the option is erroneous, throw an 128 * {@link UsageError} exception with a string describing the 129 * problem. 130 */ 131 //@ requires \nonnullelements(args); 132 //@ ensures inputEntries != null; 133 public final void processOptions(String[] args) throws UsageError { 134 inputEntries = new ArrayList(args.length); 135 processOptionsLoop(args); 136 } 137 138 //@ requires \nonnullelements(args); 139 //@ requires inputEntries != null; 140 protected final void processOptionsLoop(String[] args) throws UsageError { 141 int offset = 0; 142 143 while (offset < args.length) { 144 String s = args[offset++]; 145 if (s.length() == 0) { 146 // skip 147 } else if (s.charAt(0) == '-') { 148 offset = processOption(s, args, offset); 149 } else { 150 inputEntries.add(new UnknownInputEntry(s)); 151 } 152 } 153 } 154 155 /** 156 * Process next tool option. 157 * 158 * <p> This routine handles the standard front-end options, storing the 159 * resulting information in the preceding instance variables and 160 * <code>Info.on</code>. 161 * 162 * @design When this routine is overridden, the new method body should 163 * always end with <code>return super.processOption(option, args, 164 * offset)</code>. 165 * 166 * @param option the option currently being handled. An option 167 * always starts with a '-' character, and the remaining 168 * command-line arguments (not counting <code>option</code>) 169 * (<code>args[offset]</code>,...,<code>args[args.length-1]</code>). 170 * @param args the command-line arguments that are being processed. 171 * @param offset the offset into the <code>args</args> array that 172 * indicates which option is currently being dealt with. 173 * @return The offset to any remaining command-line arguments 174 * should be returned. (This allows the option to consume some or 175 * all of the following arguments.) 176 * @exception UsageError If the option is erroneous, throw an 177 * {@link UsageError} exception with a string describing the 178 * problem. 179 */ 180 //@ requires option != null; 181 //@ requires \nonnullelements(args); 182 //@ requires 0 <= offset && offset <= args.length; 183 //@ ensures 0 <= \result && \result <= args.length; 184 public int processOption(String option, String[] args, int offset) 185 throws UsageError { 186 option = option.toLowerCase(); 187 if (option.equals("-v") || 188 option.equals("-verbose")) { 189 javafe.util.Info.on = true; 190 return offset; 191 } else if (option.equals("-q") || 192 option.equals("-quiet")) { 193 quiet = true; 194 return offset; 195 } else if (option.equals("-nocautions")) { 196 noCautions = true; 197 return offset; 198 } else if (option.equals("-sourcepath")) { 199 checkMoreArguments(option, args, offset); 200 userSourcePath = args[offset]; 201 return offset + 1; 202 } else if (option.equals("-classpath") || 203 option.equals("-cp")) { 204 checkMoreArguments(option, args, offset); 205 userPath = args[offset]; 206 return offset + 1; 207 } else if (option.equals("-bootclasspath")) { 208 checkMoreArguments(option, args, offset); 209 sysPath = args[offset]; 210 return offset + 1; 211 } else if (option.equals("-currentdir")) { 212 checkMoreArguments(option, args, offset); 213 currentdir = args[offset]; 214 return offset + 1; 215 } else if (option.equals("-package")) { 216 checkMoreArguments(option, args, offset); 217 inputEntries.add(new PackageInputEntry(args[offset])); 218 return offset + 1; 219 } else if (option.equals("-class")) { 220 checkMoreArguments(option, args, offset); 221 inputEntries.add(new ClassInputEntry(args[offset])); 222 return offset + 1; 223 } else if (option.equals("-dir")) { 224 checkMoreArguments(option, args, offset); 225 inputEntries.add(new DirInputEntry(args[offset])); 226 return offset + 1; 227 } else if (option.equals("-file")) { 228 checkMoreArguments(option, args, offset); 229 inputEntries.add(new FileInputEntry(args[offset])); 230 return offset + 1; 231 } else if (option.equals("-list")) { 232 checkMoreArguments(option, args, offset); 233 inputEntries.add(new ListInputEntry(args[offset])); 234 return offset + 1; 235 } else if (option.equals("-f")) { 236 checkMoreArguments(option, args, offset); 237 processFileOfArgs(args[offset]); 238 return offset + 1; 239 } else if (option.equals("-source")) { 240 if ((offset >= args.length) || (args[offset].charAt(0) == '-')) { 241 throw new UsageError( 242 "Option " 243 + option 244 + " requires one argument indicating Java source version\n" 245 + "(e.g., \"-source 1.4\")"); 246 } 247 if (args[offset].equals("1.4")) 248 assertIsKeyword = true; 249 return offset + 1; 250 } else if (option.equals("-ea") || 251 option.equals("-enableassertions")) { 252 assertionsEnabled = true; 253 return offset; 254 } else if (option.equals("-da") || 255 option.equals("-disableassertions")) { 256 assertionsEnabled = false; 257 return offset; 258 } else if (option.equals("-h") || 259 option.equals("-help")) { 260 issueUsage = true; 261 return offset; 262 } else if (option.equals("-testmode")) { 263 testMode = true; 264 return offset; 265 } else if (option.equals("-showerrorlocation")) { 266 showErrorLocation = true; 267 return offset; 268 } else if (option.equals("-prefersource")) { 269 fileOrigin = PREFER_SOURCE; 270 return offset; 271 } else if (option.equals("-preferbinary")) { 272 fileOrigin = PREFER_BINARY; 273 return offset; 274 } else if (option.equals("-preferrecent")) { 275 fileOrigin = PREFER_RECENT; 276 return offset; 277 } else if (option.equals("-neverbinary")) { 278 fileOrigin = NEVER_BINARY; 279 return offset; 280 } else if (option.equals("-neversource")) { 281 fileOrigin = NEVER_SOURCE; 282 return offset; 283 } else if (option.equals("--")) { 284 while (offset < args.length) { 285 inputEntries.add(new UnknownInputEntry(args[offset++])); 286 } 287 return offset; 288 } 289 290 // Pass on unrecognized options: 291 292 // Derived classes will call: 293 // return super.processOption(option, args, offset); 294 295 // Here we inline the error processing 296 throw new UsageError("Unknown option: " + option); 297 } 298 299 //@ protected normal_behavior 300 //@ requires offset < args.length; 301 //@ also protected exceptional_behavior 302 //@ requires offset >= args.length; 303 //@ signals (Exception e) e instanceof UsageError; 304 //@ pure 305 protected void checkMoreArguments(String option, String[] args, int offset) 306 throws UsageError { 307 if (offset >= args.length) { 308 throw new UsageError("Option " + option + " requires one argument"); 309 } 310 } 311 312 public void processFileOfArgs(String filename) throws UsageError { 313 try { 314 String[] sa = new String[20]; 315 // more than most lines in the file will be, just for efficiency 316 BufferedReader r = null; 317 try { 318 r = new BufferedReader(new FileReader(filename)); 319 String s; 320 while ((s = r.readLine()) != null) { 321 sa = Utils.parseLine(s); 322 processOptionsLoop(sa); 323 } 324 } finally { 325 if (r != null) r.close(); 326 } 327 } catch (IOException e) { 328 ErrorSet.error( 329 "Failure while reading input arguments from file " 330 + filename 331 + ": " 332 + e); 333 } 334 } 335 336 /** 337 * Print our usage message to <code>System.err</code>. 338 * 339 * @param name the name of the tool whose options we are printing. 340 */ 341 public void usage(String name) { 342 System.err.print(name + ": usage: " + name + " options* "); 343 System.err.print(showNonOptions()); 344 System.err.println(" where options include:"); 345 System.err.print(showOptions(javafe.util.Info.on)); 346 } 347 348 /** 349 * @return non-option usage information in a string. 350 */ 351 public String showNonOptions() { 352 return "All switches are case-insensitive."; 353 } 354 355 /** 356 * Return option information in a string where each line is 357 * preceeded by two blank spaces and followed by a line separator. 358 * 359 * @param all if true, then all options are printed, including 360 * experimental options; otherwise, just the options expected to 361 * be used by standard users are printed. 362 * @return a String containing all option information ready for 363 * output. 364 * 365 * @usage Each overriding method should first call 366 * <code>super.showOptions()</code>. 367 */ 368 public String showOptions(boolean all) { 369 String result = showOptionArray(publicOptionData); 370 if (all) result += showOptionArray(privateOptionData); 371 return result; 372 } 373 374 public String showOptionArray(String[][] data) { 375 StringBuffer sb = new StringBuffer(); 376 for (int i = 0; i < data.length; ++i) { 377 sb.append(format(data[i])); 378 } 379 return sb.toString(); 380 } 381 382 public String format(String[] sa) { 383 int columns = Integer.getInteger("COLUMNS", 80).intValue(); 384 StringBuffer sb = new StringBuffer(" " + sa[0] + " : "); 385 int current_column = 2 + sa[0].length() + 3; 386 // find the most words that fit into columns-(2 + sa[0].length + 3) 387 // for first line. thereafter, find the most words that fit into 388 // (columns-8) lines until there is no remaining words. 389 StringTokenizer st = new StringTokenizer(sa[1]); 390 while (st.hasMoreTokens()) { 391 String word = st.nextToken(); 392 if (current_column + word.length() < columns) { 393 sb.append(word + " "); 394 current_column += word.length() + 1; 395 } 396 else { 397 sb.append(eol + "\t" + word + " "); 398 current_column = 9 + word.length(); 399 } 400 } 401 sb.append(eol); 402 return sb.toString(); 403 } 404 405 final String[][] publicOptionData = { 406 { "-Help, -h", 407 "Prints this usage message and terminates (combine with -v to see private\n" + 408 "\tswitches)" }, 409 { "-Verbose, -v", 410 "verbose mode" }, 411 { "-Quiet, -q", 412 "quiet mode (no informational messages)" }, 413 { "-BootClassPath <classpath>", 414 "Directory path for specification and class files for the current JDK (default is the built-in classpath of your JDK); prepended to the current classpath. Multiple uses of -BootClassPath are ignored; only final use of -BootClassPath is recognized, as in javac." }, 415 { "-Class <fully.specified.classname>", 416 "Check the specified class; this option can be specified multiple times" }, 417 { "-ClassPath <classpath>, -cp <classpath>", 418 "Directory path for class files (default is value of CLASSPATH). Multiple uses of -ClassPath are ignored; only final use of -ClassPath is recognized, as in javac." }, 419 { "-DisableAssertions, -da", 420 "Ignores all Java assert statements" }, 421 { "-dir <directory>", 422 "Check all Java files in the specified directory" }, 423 { "-EnableAssertions, -ea", 424 "Processes all Java assert statements" }, 425 { "-f <file containing command-line arguments>", 426 "Path to a file containing command-line arguments that are inserted at this point in the command-line"}, 427 { "-File <filename>", 428 "Check all classes in the specified file <filename>" }, 429 { "-List <filename>", 430 "Check all classes listed in the text file <filename>; each classname in the file should be fully specified and end-of-line terminated" }, 431 { "-NoCautions", 432 "Does not print messages that are simply cautions" }, 433 { "-Package <packagename>", 434 "Loads all the files in the named package" }, 435 { "-Source <release>", 436 "Provide source compatibility with specified release" }, 437 { "-SourcePath <classpath>", 438 "Directory path for source files (default is classpath). Multiple uses of -SourcePath are ignored; only final use of -SourcePath is recognized, as in javac." }, 439 }; 440 441 final String[][] privateOptionData = { 442 { "-CurrentDir <directory>", 443 "Specify the current working directory (REMOVE THIS OPTION?)" }, 444 { "-TestMode", 445 "Replaces execution time by a constant string and path separators by `|'\n" + 446 "so oracle files can be used in automated testing" }, 447 { "-NeverBinary", 448 "Never read type information from a class files" }, 449 { "-NeverSource", 450 "Never read type information from source files" }, 451 { "-PreferBinary", 452 "Read type information from class files even if they are out-of-date" }, 453 { "-PreferRecent", 454 "Read type information from either class or source files, whichever is\n" + 455 "most recent" }, 456 { "-PreferSource", 457 "Read type information from source if it is available, otherwise use\n" + 458 "class files" }, 459 { "-ShowErrorLocation", 460 "Dump a stacktrace to standard error when reporting a warning or error" }, 461 }; 462 463 final static public String eol = System.getProperty("line.separator"); 464 }