    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    }