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    }