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 //alx: dw if we have to recognize and check the Universe type modifiers
119 public boolean useUniverseTypeSystem = false;
120 // the next variable decodes where the modifiers can be and if one has to check them
121 // modifiers in comments => mod 2 == 0
122 // modifiers as keyword => mod 3 == 0
123 // no checking => mod 5 == 0
124 public int universeLevel = 23; // just some arbitrary prime larger then the primes used as universe modes
125 //alx-end
126
127 public Options() {
128 // Everything should be initialized to default values.
129 javafe.util.Info.on = false;
130 }
131
132 /**
133 * Process tool options contained in <code>args</code>.
134 *
135 * @param args the command-line arguments that are being processed.
136 * @exception UsageError If the option is erroneous, throw an
137 * {@link UsageError} exception with a string describing the
138 * problem.
139 */
140 //@ requires \nonnullelements(args);
141 //@ ensures inputEntries != null;
142 public final void processOptions(String[] args) throws UsageError {
143 inputEntries = new ArrayList(args.length);
144 processOptionsLoop(args);
145 }
146
147 //@ requires \nonnullelements(args);
148 //@ requires inputEntries != null;
149 protected final void processOptionsLoop(String[] args) throws UsageError {
150 int offset = 0;
151
152 while (offset < args.length) {
153 String s = args[offset++];
154 if (s.length() == 0) {
155 // skip
156 } else if (s.charAt(0) == '-') {
157 offset = processOption(s, args, offset);
158 } else {
159 inputEntries.add(new UnknownInputEntry(s));
160 }
161 }
162 }
163
164 /**
165 * Process next tool option.
166 *
167 * <p> This routine handles the standard front-end options, storing the
168 * resulting information in the preceding instance variables and
169 * <code>Info.on</code>.
170 *
171 * @design When this routine is overridden, the new method body should
172 * always end with <code>return super.processOption(option, args,
173 * offset)</code>.
174 *
175 * @param option the option currently being handled. An option
176 * always starts with a '-' character, and the remaining
177 * command-line arguments (not counting <code>option</code>)
178 * (<code>args[offset]</code>,...,<code>args[args.length-1]</code>).
179 * @param args the command-line arguments that are being processed.
180 * @param offset the offset into the <code>args</args> array that
181 * indicates which option is currently being dealt with.
182 * @return The offset to any remaining command-line arguments
183 * should be returned. (This allows the option to consume some or
184 * all of the following arguments.)
185 * @exception UsageError If the option is erroneous, throw an
186 * {@link UsageError} exception with a string describing the
187 * problem.
188 */
189 //@ requires option != null;
190 //@ requires \nonnullelements(args);
191 //@ requires 0 <= offset && offset <= args.length;
192 //@ ensures 0 <= \result && \result <= args.length;
193 public int processOption(String option, String[] args, int offset)
194 throws UsageError {
195 option = option.toLowerCase();
196 if (option.equals("-v") ||
197 option.equals("-verbose")) {
198 javafe.util.Info.on = true;
199 return offset;
200 } else if (option.equals("-q") ||
201 option.equals("-quiet")) {
202 quiet = true;
203 return offset;
204 } else if (option.equals("-nocautions")) {
205 noCautions = true;
206 return offset;
207 } else if (option.equals("-sourcepath")) {
208 checkMoreArguments(option, args, offset);
209 userSourcePath = args[offset];
210 return offset + 1;
211 } else if (option.equals("-classpath") ||
212 option.equals("-cp")) {
213 checkMoreArguments(option, args, offset);
214 userPath = args[offset];
215 return offset + 1;
216 } else if (option.equals("-bootclasspath")) {
217 checkMoreArguments(option, args, offset);
218 sysPath = args[offset];
219 return offset + 1;
220 } else if (option.equals("-currentdir")) {
221 checkMoreArguments(option, args, offset);
222 currentdir = args[offset];
223 return offset + 1;
224 } else if (option.equals("-package")) {
225 checkMoreArguments(option, args, offset);
226 inputEntries.add(new PackageInputEntry(args[offset]));
227 return offset + 1;
228 } else if (option.equals("-class")) {
229 checkMoreArguments(option, args, offset);
230 inputEntries.add(new ClassInputEntry(args[offset]));
231 return offset + 1;
232 } else if (option.equals("-dir")) {
233 checkMoreArguments(option, args, offset);
234 inputEntries.add(new DirInputEntry(args[offset]));
235 return offset + 1;
236 } else if (option.equals("-file")) {
237 checkMoreArguments(option, args, offset);
238 inputEntries.add(new FileInputEntry(args[offset]));
239 return offset + 1;
240 } else if (option.equals("-list")) {
241 checkMoreArguments(option, args, offset);
242 inputEntries.add(new ListInputEntry(args[offset]));
243 return offset + 1;
244 } else if (option.equals("-f")) {
245 checkMoreArguments(option, args, offset);
246 processFileOfArgs(args[offset]);
247 return offset + 1;
248 } else if (option.equals("-source")) {
249 if ((offset >= args.length) || (args[offset].charAt(0) == '-')) {
250 throw new UsageError(
251 "Option "
252 + option
253 + " requires one argument indicating Java source version\n"
254 + "(e.g., \"-source 1.4\")");
255 }
256 if (args[offset].equals("1.4"))
257 assertIsKeyword = true;
258 return offset + 1;
259 } else if (option.equals("-ea") ||
260 option.equals("-enableassertions")) {
261 assertionsEnabled = true;
262 return offset;
263 } else if (option.equals("-da") ||
264 option.equals("-disableassertions")) {
265 assertionsEnabled = false;
266 return offset;
267 } else if (option.equals("-h") ||
268 option.equals("-help")) {
269 issueUsage = true;
270 return offset;
271 } else if (option.equals("-testmode")) {
272 testMode = true;
273 return offset;
274 } else if (option.equals("-showerrorlocation")) {
275 showErrorLocation = true;
276 return offset;
277 } else if (option.equals("-prefersource")) {
278 fileOrigin = PREFER_SOURCE;
279 return offset;
280 } else if (option.equals("-preferbinary")) {
281 fileOrigin = PREFER_BINARY;
282 return offset;
283 } else if (option.equals("-preferrecent")) {
284 fileOrigin = PREFER_RECENT;
285 return offset;
286 } else if (option.equals("-neverbinary")) {
287 fileOrigin = NEVER_BINARY;
288 return offset;
289 } else if (option.equals("-neversource")) {
290 fileOrigin = NEVER_SOURCE;
291 return offset;
292 //alx: dw recoginze options
293 } else if (option.equals("-universes")) {
294 useUniverseTypeSystem=true;
295 universeLevel=2;
296 if ((offset < args.length) && (args[offset].charAt(0) != '-')) {
297 if (args[offset].equalsIgnoreCase("comment")) {
298 universeLevel=2;
299 offset++;
300 }
301 else if (args[offset].equalsIgnoreCase("keyword")) {
302 universeLevel=3;
303 offset++;
304 }
305 else if (args[offset].equalsIgnoreCase("commentandkeyword")) {
306 universeLevel=6;
307 offset++;
308 }
309 if ((offset < args.length) &&
310 (args[offset].charAt(0) != '-') &&
311 args[offset].equalsIgnoreCase("readonlyforpurector")) {
312 universeLevel*=7;
313 offset++;
314 }
315 if ((offset < args.length) &&
316 (args[offset].charAt(0) != '-') &&
317 args[offset].equalsIgnoreCase("nochecks")) {
318 universeLevel*=5;
319 offset++;
320 }
321 }
322 return offset;
323 //alx-end
324 } else if (option.equals("--")) {
325 while (offset < args.length) {
326 inputEntries.add(new UnknownInputEntry(args[offset++]));
327 }
328 return offset;
329 }
330
331 // Pass on unrecognized options:
332
333 // Derived classes will call:
334 // return super.processOption(option, args, offset);
335
336 // Here we inline the error processing
337 throw new UsageError("Unknown option: " + option);
338 }
339
340 //@ protected normal_behavior
341 //@ requires offset < args.length;
342 //@ also protected exceptional_behavior
343 //@ requires offset >= args.length;
344 //@ signals (Exception e) e instanceof UsageError;
345 //@ pure
346 protected void checkMoreArguments(String option, String[] args, int offset)
347 throws UsageError {
348 if (offset >= args.length) {
349 throw new UsageError("Option " + option + " requires one argument");
350 }
351 }
352
353 public void processFileOfArgs(String filename) throws UsageError {
354 try {
355 String[] sa = new String[20];
356 // more than most lines in the file will be, just for efficiency
357 BufferedReader r = null;
358 try {
359 r = new BufferedReader(new FileReader(filename));
360 String s;
361 while ((s = r.readLine()) != null) {
362 sa = Utils.parseLine(s);
363 processOptionsLoop(sa);
364 }
365 } finally {
366 if (r != null) r.close();
367 }
368 } catch (IOException e) {
369 ErrorSet.error(
370 "Failure while reading input arguments from file "
371 + filename
372 + ": "
373 + e);
374 }
375 }
376
377 /**
378 * Print our usage message to <code>System.err</code>.
379 *
380 * @param name the name of the tool whose options we are printing.
381 */
382 public void usage(String name) {
383 System.err.print(name + ": usage: " + name + " options* ");
384 System.err.print(showNonOptions());
385 System.err.println(" where options include:");
386 System.err.print(showOptions(javafe.util.Info.on));
387 }
388
389 /**
390 * @return non-option usage information in a string.
391 */
392 public String showNonOptions() {
393 return "All switches are case-insensitive.";
394 }
395
396 /**
397 * Return option information in a string where each line is
398 * preceeded by two blank spaces and followed by a line separator.
399 *
400 * @param all if true, then all options are printed, including
401 * experimental options; otherwise, just the options expected to
402 * be used by standard users are printed.
403 * @return a String containing all option information ready for
404 * output.
405 *
406 * @usage Each overriding method should first call
407 * <code>super.showOptions()</code>.
408 */
409 public String showOptions(boolean all) {
410 String result = showOptionArray(publicOptionData);
411 if (all) result += showOptionArray(privateOptionData);
412 return result;
413 }
414
415 public String showOptionArray(String[][] data) {
416 StringBuffer sb = new StringBuffer();
417 for (int i = 0; i < data.length; ++i) {
418 sb.append(format(data[i]));
419 }
420 return sb.toString();
421 }
422
423 public String format(String[] sa) {
424 int columns = Integer.getInteger("COLUMNS", 80).intValue();
425 StringBuffer sb = new StringBuffer(" " + sa[0] + " : ");
426 int current_column = 2 + sa[0].length() + 3;
427 // find the most words that fit into columns-(2 + sa[0].length + 3)
428 // for first line. thereafter, find the most words that fit into
429 // (columns-8) lines until there is no remaining words.
430 StringTokenizer st = new StringTokenizer(sa[1]);
431 while (st.hasMoreTokens()) {
432 String word = st.nextToken();
433 if (current_column + word.length() < columns) {
434 sb.append(word + " ");
435 current_column += word.length() + 1;
436 }
437 else {
438 sb.append(eol + "\t" + word + " ");
439 current_column = 9 + word.length();
440 }
441 }
442 sb.append(eol);
443 return sb.toString();
444 }
445
446 final String[][] publicOptionData = {
447 { "-Help, -h",
448 "Prints this usage message and terminates (combine with -v to see private\n" +
449 "\tswitches)" },
450 { "-Verbose, -v",
451 "verbose mode" },
452 { "-Quiet, -q",
453 "quiet mode (no informational messages)" },
454 { "-BootClassPath <classpath>",
455 "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." },
456 { "-Class <fully.specified.classname>",
457 "Check the specified class; this option can be specified multiple times" },
458 { "-ClassPath <classpath>, -cp <classpath>",
459 "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." },
460 { "-DisableAssertions, -da",
461 "Ignores all Java assert statements" },
462 { "-dir <directory>",
463 "Check all Java files in the specified directory" },
464 { "-EnableAssertions, -ea",
465 "Processes all Java assert statements" },
466 { "-f <file containing command-line arguments>",
467 "Path to a file containing command-line arguments that are inserted at this point in the command-line"},
468 { "-File <filename>",
469 "Check all classes in the specified file <filename>" },
470 { "-List <filename>",
471 "Check all classes listed in the text file <filename>; each classname in the file should be fully specified and end-of-line terminated" },
472 { "-NoCautions",
473 "Does not print messages that are simply cautions" },
474 { "-Package <packagename>",
475 "Loads all the files in the named package" },
476 { "-Source <release>",
477 "Provide source compatibility with specified release" },
478 { "-SourcePath <classpath>",
479 "Directory path for source files (default is classpath). Multiple uses of -SourcePath are ignored; only final use of -SourcePath is recognized, as in javac." },
480 };
481
482 final String[][] privateOptionData = {
483 { "-CurrentDir <directory>",
484 "Specify the current working directory (REMOVE THIS OPTION?)" },
485 { "-TestMode",
486 "Replaces execution time by a constant string and path separators by `|'\n" +
487 "so oracle files can be used in automated testing" },
488 { "-NeverBinary",
489 "Never read type information from a class files" },
490 { "-NeverSource",
491 "Never read type information from source files" },
492 { "-PreferBinary",
493 "Read type information from class files even if they are out-of-date" },
494 { "-PreferRecent",
495 "Read type information from either class or source files, whichever is\n" +
496 "most recent" },
497 { "-PreferSource",
498 "Read type information from source if it is available, otherwise use\n" +
499 "class files" },
500 { "-ShowErrorLocation",
501 "Dump a stacktrace to standard error when reporting a warning or error" },
502 };
503
504 final static public String eol = System.getProperty("line.separator");
505 }