001    // The four files
002    //   Option.java
003    //   OptionGroup.java
004    //   Options.java
005    //   Unpublicized.java
006    // together comprise the implementation of command-line processing.
007    
008    package daikon.util;
009    
010    import java.io.*;
011    import java.util.*;
012    import java.util.regex.*;
013    import java.lang.reflect.*;
014    import java.lang.annotation.*;
015    import com.sun.javadoc.Doc;
016    import com.sun.javadoc.RootDoc;
017    import com.sun.javadoc.ClassDoc;
018    import com.sun.javadoc.FieldDoc;
019    import com.sun.javadoc.Tag;
020    import com.sun.javadoc.SeeTag;
021    
022    /**
023     * The Options class parses command-line options and sets fields in your
024     * program accordingly.  Each field that is annotated with @{@link
025     * plume.Option} is set from a command-line argument of the same name.
026     * The Options class can also create usage messages and HTML documentation.
027     * The Options class interprets annotations and Javadoc comments, so that
028     * you do not have to write duplicative, boilerplate code and
029     * documentation that could get out of sync with the rest of your program.
030     * <p>
031     *
032     * The main entry point is {@link #parse_or_usage(String[])}.
033     * Typical use in your program is:
034     * <!-- Maybe expand this example a bit. -->
035     * <pre>
036     *    public static void main(String[] args) {
037     *      MyProgram myInstance = new MyProgram();
038     *      Options options = new Options("MyProgram [options] infile outfile",
039     *                                    myInstance, MyUtilityClass.class);
040     *      // Sets fields in object myInstance, and sets static fields in
041     *      // class MyUtilityClass.
042     *      // Returns the original command line, with all options removed.
043     *      String[] file_args = options.parse_or_usage (args);
044     *      ...
045     * </pre>
046     *
047     * The @{@link Option} annotation on a field specifies user documentation
048     * and, optionally, a one-character short name that users may supply on the
049     * command line.  The long name is taken from the name of the variable;
050     * when the name contains an underscore, the user may substitute a hyphen on
051     * the command line instead. <p>
052     *
053     * On the command line, the values for options are specified in the form
054     * '--longname=value', '-shortname=value', '--longname value', or '-shortname
055     * value'.  If {@link #use_single_dash(boolean)} is true, then the long names
056     * take the form '-longname=value' or '-longname value'.  The value is
057     * mandatory for all options except booleans.  Booleans are set to true if no
058     * value is specified. <p>
059     *
060     * All arguments that start with '-' are processed as options.  To
061     * terminate option processing at the first non-option argument, see {@link
062     * #parse_options_after_arg(boolean)}.  Also, the special option '--'
063     * terminates option processing; all subsequent arguments are passed to the
064     * program (along with any preceding non-option arguments) without being
065     * scanned for options. <p>
066     *
067     * A user may provide an option multiple times on the command line.  If the
068     * field is a list, each entry is be added to the list.  If the field is
069     * not a list, then only the last occurrence is used (subsequent
070     * occurrences overwrite the previous value). <p>
071     *
072     * <b>Unpublicized options</b> <p>
073     * The @{@link Unpublicized} annotation causes an option not to be displayed
074     * in the usage message.  This can be useful for options that are
075     * preliminary, experimental, or for internal purposes only.  The @{@link
076     * Unpublicized} annotation must be specified in addition to the @{@link
077     * Option} annotation. <p> 
078     *
079     * <b>Option groups</b> <p>
080     * The @{@link OptionGroup} annotation can be used to assign a name to a set of
081     * related options.  This is useful for providing organization when working with
082     * many options.  Options in the same group are displayed under the same heading
083     * in usage texts.  Option groups themselves can be unpublicized causing the
084     * set of options belonging to the group to not be displayed in the default
085     * usage message. <p>
086     *
087     * The @{@link OptionGroup} annotation must be specified on a field in addition
088     * to an @{@link Option} annotation.  The <code>@OptionGroup</code> annotation
089     * acts like a delimiter&#151;all <code>@Option</code>-annotated fields up to
090     * the next <code>@OptionGroup</code> annotation belong to the same group.
091     * When using option groups, the first <code>@Option</code>-annotated field of
092     * every class and object passed to the {@link #Options(String, Object...)}
093     * constructor must have an <code>@OptionGroup</code> annotation.  Furthermore,
094     * the first parameter of an <code>@OptionGroup</code> annotation (the group
095     * name) must be unique among all classes and objects passed to the {@link
096     * #Options(String, Object...)} constructor. <p>
097     *
098     * When an @{@link Unpublicized} annotation is used on a field that is in an
099     * unpublicized option group, that field is excluded in <b>all</b> usage
100     * messages, even when passing the group's name explicitly as a parameter to
101     * {@link #usage(String...)}. <p>
102     *
103     * <b>Option aliases</b> <p>
104     * The @{@link Option} annotation has an optional parameter <code>aliases</code>
105     * which accepts an array of strings.  Each string in the array is an alias for
106     * the option being defined and can be used in place of an option's long name
107     * or short name.  Aliases should start with a single dash or double dash.  It
108     * is the user's responsibility to ensure that aliases does not cause ambiguity
109     * and do not collide with other options. <p>
110     *
111     * Note that parameters must be named when passing more than one parameter to
112     * an annotation, as in the following examples. <p>
113     * For option groups:
114     * <pre>
115     *     &#64;OptionGroup(value="Debugging Options", unpublicized=true)
116     * </pre>
117     * For option aliases:
118     * <pre>
119     *     &#64;Option(value="-h Print the detailed help", aliases={"-help", "--help"})
120     * </pre>
121     *
122     * <b>Supported field types</b> <p>
123     * The field may be of the following types:
124     * <ul>
125     *   <li>Primitive types:  boolean, int, long, float, double.
126     *       (Primitives can also be represented as wrappers (Boolean,
127     *       Integer, Long, Float, Double).  Use of a wrapper type allows the
128     *       argument to have no default value.)
129     *   <li>Reference types that have a constructor with a single string
130     *       parameter.
131     *   <li>java.util.regex.Pattern.
132     *   <li>Lists of any supported reference type.  Lists must be initialized
133     *       to a valid list (e.g., the empty list) before using Options on
134     *       that list.
135     * </ul> <p>
136     *
137     * <b>Example:</b> <p>
138     *
139     * <!-- Example needs some more words of explanation and example command lines. -->
140     * <!-- Given this code: --> <pre>
141     *
142     *  public static class Test {
143     *
144     *    &#64;Option ("-o &lt;filename&gt; the output file ")
145     *    public static File outfile = new File("/tmp/foobar");
146     *
147     *    &#64;Option ("-i ignore case")
148     *    public static boolean ignore_case;
149     *
150     *    &#64;Option ("-t set the initial temperature")
151     *    public static double temperature = 75.0;
152     *
153     *    public static void main (String[] args) {
154     *      Options options = new Options ("Test [options] files", new Test());
155     *      String[] file_args = options.parse_or_usage (args);
156     *    }
157     *  }
158     *</pre>
159     *
160     * For an example of this library being used in practice see {@link
161     * plume.Lookup}. <p>
162     *
163     * <b>Limitations</b> <ul>
164     *
165     *  <li> Short options are only supported as separate entries
166     *  (e.g., "-a -b") and not as a single group (e.g., "-ab").
167     *
168     *  <li> Not all primitive types are supported.
169     *
170     *  <li> Types without a string constructor are not supported.
171     *
172     *  <li> The "--no-long" option to turn off a boolean option named "long"
173     *  is not supported; use "--long=false" instead.
174     *
175     * </ul>
176     *
177     * <b>Possible enhancements</b> <ul>
178     *  <li> Positional arguments (non-options that must be provided in a given
179     *  order) could be supported.
180     * </ul>
181     *
182     * @see plume.Option
183     * @see plume.OptionGroup
184     * @see plume.Unpublicized
185     **/
186    public class Options {
187      
188      @SuppressWarnings("nullness") // line.separator property always exists
189      private static String eol = System.getProperty("line.separator");
190    
191      /** Information about an option **/
192      private class OptionInfo {
193    
194        /** Field containing the value of the option **/
195        Field field;
196    
197        /** Option information for the field **/
198        Option option;
199    
200        /** Object containing the field.  Null if the field is static. **/
201        /*@Nullable*/ Object obj;
202    
203        /** Short (one character) argument name **/
204        /*@Nullable*/ String short_name;
205    
206        /** Long argument name **/
207        String long_name;
208    
209        /** Aliases for this option **/
210        String[] aliases;
211    
212        /** Argument description **/
213        String description;
214    
215        /** Javadoc description **/
216        /*@Nullable*/ String jdoc;
217    
218        /**
219         * Name of the argument type.  Defaults to the type of the field, but
220         * user can override this in the option string.
221         */
222        String type_name;
223    
224        /**
225         * Class type of this field.  If the field is a list, the basetype
226         * of the list.
227         */
228        Class<?> base_type;
229    
230        /** Default value of the option as a string **/
231        /*@Nullable*/ String default_str = null;
232    
233        /** If the option is a list, this references that list. **/
234        /*@LazyNonNull*/ List<Object> list = null;
235    
236        /** Constructor that takes one String for the type **/
237        /*@Nullable*/ Constructor<?> constructor = null;
238    
239        /** Factory that takes a string (some classes don't have a string constructor) and always returns non-null. */
240        /*@Nullable*/ Method factory = null;
241    
242        /**
243         * If true, this OptionInfo is not output when printing documentation.
244         * @see #usage()
245         */
246        boolean unpublicized;
247    
248        /**
249         * Create the specified option.  If obj is null, the field must be
250         * static.  The short name, type name, and description are taken
251         * from the option annotation.  The long name is the name of the
252         * field.  The default value is the current value of the field.
253         */
254        OptionInfo (Field field, Option option, /*@Nullable*/ Object obj, boolean unpublicized) {
255          this.field = field;
256          this.option = option;
257          this.obj = obj;
258          this.base_type = field.getType();
259          this.unpublicized = unpublicized;
260          this.aliases = option.aliases();
261    
262          // The long name is the name of the field
263          long_name = field.getName();
264          if (use_dashes)
265            long_name = long_name.replace ('_', '-');
266    
267          // Get the default value (if any)
268          Object default_obj = null;
269          if (!Modifier.isPublic (field.getModifiers()))
270            throw new Error ("option field is not public: " + field);
271          try {
272            default_obj = field.get (obj);
273            if (default_obj != null)
274              default_str = default_obj.toString();
275          } catch (Exception e) {
276            throw new Error ("Unexpected error getting default for " + field, e);
277          }
278    
279          // Handle lists.  When a list argument is specified multiple times,
280          // each argument value is appended to the list.
281          Type gen_type = field.getGenericType();
282          if (gen_type instanceof ParameterizedType) {
283            ParameterizedType pt = (ParameterizedType) gen_type;
284            Type raw_type = pt.getRawType();
285            if (!raw_type.equals (List.class))
286              throw new Error ("@Option does not support type " + pt + " for field " + field);
287            if (default_obj == null)
288              throw new Error ("List option " + field + " must be initialized");
289            @SuppressWarnings("unchecked")
290            List<Object> default_obj_as_list = (List<Object>) default_obj;
291            this.list = default_obj_as_list;
292            // System.out.printf ("list default = %s%n", list);
293            this.base_type = (Class<?>) pt.getActualTypeArguments()[0];
294    
295            // System.out.printf ("Param type for %s = %s%n", field, pt);
296            // System.out.printf ("raw type = %s, type = %s%n", pt.getRawType(),
297            //                   pt.getActualTypeArguments()[0]);
298          }
299    
300          // Get the short name, type name, and description from the annotation
301          ParseResult pr = parse_option (option.value());
302          short_name = pr.short_name;
303          if (pr.type_name != null) {
304            type_name = pr.type_name;
305          } else {
306            type_name = type_short_name (base_type);
307            if (list != null)
308              type_name += "[]";
309          }
310          description = pr.description;
311    
312          // Get a constructor for non-primitive base types
313          if (!base_type.isPrimitive() && !base_type.isEnum()) {
314            try {
315              if (base_type == Pattern.class) {
316                factory = Pattern.class.getMethod ("compile", String.class);
317              } else { // look for a string constructor
318                assert base_type != null; // nullness checker: problem with flow
319                constructor = base_type.getConstructor (String.class);
320              }
321            } catch (Exception e) {
322              throw new Error ("Option " + field
323                               + " does not have a string constructor", e);
324            }
325          }
326        }
327    
328        /**
329         * Returns whether or not this option has a required argument.
330         */
331        public boolean argument_required() {
332          Class<?> type = field.getType();
333          return ((type != Boolean.TYPE) && (type != Boolean.class));
334        }
335    
336        /**
337         * Returns a short synopsis of the option in the form
338         * -s --long=<type>
339         * <strong>or</strong>
340         * -s -long=<type>
341         * if use_single_dash is true.
342         **/
343        public String synopsis() {
344          String prefix = use_single_dash ? "-" : "--";
345          String name = prefix + long_name;
346          if (short_name != null)
347            name = String.format ("-%s %s", short_name, name);
348          name += String.format ("=<%s>", type_name);
349          return (name);
350        }
351    
352        /**
353         * Returns a one-line description of the option.
354         */
355        public String toString() {
356          String prefix = use_single_dash ? "-" : "--";
357          String short_name_str = "";
358          if (short_name != null)
359            short_name_str = "-" + short_name + " ";
360          return String.format ("%s%s%s field %s", short_name_str, prefix,
361                                long_name, field);
362        }
363    
364        /** Returns the class that declares this option. **/
365        public Class<?> get_declaring_class() {
366          return field.getDeclaringClass();
367        }
368      }
369    
370      /** Information about an option group **/
371      private class OptionGroupInfo {
372    
373        /** The name of this option group **/
374        String name;
375    
376        /** If true, this group of options will not be printed in usage output by
377         * default. However, the usage information for this option group can be
378         * printed by specifying the group explicitly in the call to {@link
379         * #usage}.
380         */
381        boolean unpublicized;
382    
383        /** List of options that belong to this group **/
384        List<OptionInfo> optionList;
385    
386        OptionGroupInfo(String name, boolean unpublicized) {
387          optionList = new ArrayList<OptionInfo>();
388          this.name = name;
389          this.unpublicized = unpublicized;
390        }
391    
392        OptionGroupInfo(OptionGroup optionGroup) {
393          optionList = new ArrayList<OptionInfo>();
394          this.name = optionGroup.value();
395          this.unpublicized = optionGroup.unpublicized();
396        }
397    
398      }
399    
400    
401      /**
402       * Whether to parse options after a non-option command-line argument.
403       * @see #parse_options_after_arg(boolean)
404       **/
405      private boolean parse_options_after_arg = true;
406    
407      /** All of the argument options as a single string **/
408      private String options_str = "";
409    
410      /** First specified class.  Void stands for "not yet initialized". **/
411      private Class<?> main_class = Void.TYPE;
412    
413      /** List of all of the defined options **/
414      private List<OptionInfo> options = new ArrayList<OptionInfo>();
415    
416      /** Map from short or long option names (with leading dashes) to option information **/
417      private Map<String,OptionInfo> name_map
418        = new LinkedHashMap<String,OptionInfo>();
419    
420      /** Map from option group name to option group information **/
421      private Map<String, OptionGroupInfo> group_map
422        = new LinkedHashMap<String, OptionGroupInfo>();
423    
424      /**
425       * If, after the Options constructor is called, use_groups is true, then the
426       * user is using @OptionGroup annotations correctly (as per the requirement
427       * specified above).  If false, then @OptionGroup annotations have not been
428       * specified on any @Option-annotated fields.  When @OptionGroup annotations
429       * are used incorrectly, an Error is thrown by the Options constructor.
430       */
431      private boolean use_groups;
432    
433      /**
434       * Convert underscores to dashes in long options in usage messages.  Users
435       * may specify either the underscore or dashed name on the command line.
436       */
437      private boolean use_dashes = true;
438    
439      /**
440       * When true, long options take the form -longOption with a single dash,
441       * rather than the default --longOption with two dashes.
442       */
443      private boolean use_single_dash = false;
444    
445      @Option ("Split arguments to lists on blanks")
446      public static boolean split_lists = false;
447    
448      /**
449       * Synopsis of usage.  Example:  "prog [options] arg1 arg2 ..."
450       * <p>
451       * This variable is public so that clients can reset it (useful for
452       * masquerading as another program, based on parsed options).
453       **/
454      public /*@Nullable*/ String usage_synopsis = null;
455    
456      // Debug loggers
457      private SimpleLog debug_options = new SimpleLog (false);
458    
459      /**
460       * Prepare for option processing.  Creates an object that will set fields
461       * in all the given arguments.  An argument to this method may be a
462       * Class, in which case its static fields are set.  The names of all the
463       * options (that is, the fields annotated with &#064;{@link Option}) must be
464       * unique across all the arguments.
465       */
466      public Options (Object... args) {
467        this ("", args);
468      }
469    
470      /**
471       * Prepare for option processing.  Creates an object that will set fields
472       * in all the given arguments.  An argument to this method may be a
473       * Class, in which case its static fields are set.  The names of all the
474       * options (that is, the fields annotated with &#064;{@link Option}) must be
475       * unique across all the arguments.
476       * @param usage_synopsis A synopsis of how to call your program
477       */
478      public Options (String usage_synopsis, Object... args) {
479    
480        if (args.length == 0) {
481          throw new Error("Must pass at least one object to Options constructor");
482        }
483    
484        this.usage_synopsis = usage_synopsis;
485        
486        this.use_groups = false;
487    
488        // true once the first @Option annotation is observed, false until then.
489        boolean seen_first_opt = false;
490    
491        // Loop through each specified object or class
492        for (Object obj : args) {
493          boolean is_class = obj instanceof Class<?>;
494          String current_group = null;
495    
496          Field[] fields;
497          if (is_class) {
498            if (main_class == Void.TYPE)
499              main_class = (Class<?>) obj;
500            fields = ((Class<?>) obj).getDeclaredFields();
501          } else {
502            if (main_class == Void.TYPE)
503              main_class = obj.getClass();
504            fields = obj.getClass().getDeclaredFields();
505          }
506    
507          for (Field f : fields) {
508            debug_options.log ("Considering field %s of object %s with annotations %s%n",
509                               f, obj, Arrays.toString(f.getDeclaredAnnotations()));
510            Option option = safeGetAnnotation(f, Option.class);
511            if (option == null)
512              continue;
513    
514            boolean unpublicized = safeGetAnnotation(f, Unpublicized.class) != null;
515    
516            if (is_class && !Modifier.isStatic (f.getModifiers()))
517              throw new Error ("non-static option " + f + " in class " + obj);
518    
519            OptionInfo oi = new OptionInfo(f, option, is_class ? null : obj, unpublicized);
520            options.add(oi);
521    
522            OptionGroup optionGroup = safeGetAnnotation(f, OptionGroup.class);
523    
524            if (!seen_first_opt) {
525              seen_first_opt = true;
526              // This is the first @Option annotation encountered so we can decide
527              // now if the user intends to use option groups.
528              if (optionGroup != null)
529                use_groups = true;
530              else
531                continue;
532            }
533    
534            if (!use_groups) {
535              if (optionGroup != null)
536                // The user included an @OptionGroup annotation in their code
537                // without including an @OptionGroup annotation on the first
538                // @Option-annotated field, hence violating the requirement.
539    
540                // NOTE: changing this error string requires changes to TestPlume
541                throw new Error("missing @OptionGroup annotation on the first " +
542                                "@Option-annotated field of class " + main_class);
543              else
544                continue;
545            }
546    
547            // use_groups is true at this point.  The variable current_group is set
548            // to null at the start of every iteration through 'args'.  This is so
549            // we can check that the first @Option-annotated field of every
550            // class/object in 'args' has an @OptionGroup annotation when use_groups
551            // is true, as required.
552            if (current_group == null && optionGroup == null) {
553              // NOTE: changing this error string requires changes to TestPlume
554              throw new Error("missing @OptionGroup annotation in field "
555                              + f + " of class " + obj);
556            } else if (optionGroup != null) {
557              String name = optionGroup.value();
558              if (group_map.containsKey(name))
559                  throw new Error("option group " + name + " declared twice");
560              OptionGroupInfo gi = new OptionGroupInfo(optionGroup);
561              group_map.put(name, gi);
562              current_group = name;
563            } // current_group is non-null at this point
564            @SuppressWarnings("nullness") // map key
565            /*@NonNull*/ OptionGroupInfo ogi = group_map.get(current_group);
566            ogi.optionList.add(oi);
567    
568          } // loop through fields
569        } // loop through args
570    
571        String prefix = use_single_dash ? "-" : "--";
572    
573        // Add each option to the option name map
574        for (OptionInfo oi : options) {
575          if (oi.short_name != null) {
576            if (name_map.containsKey ("-" + oi.short_name))
577              throw new Error ("short name " + oi + " appears twice");
578            name_map.put ("-" + oi.short_name, oi);
579          }
580          if (name_map.containsKey (prefix + oi.long_name))
581            throw new Error ("long name " + oi + " appears twice");
582          name_map.put (prefix + oi.long_name, oi);
583          if (use_dashes && oi.long_name.contains ("-"))
584            name_map.put (prefix + oi.long_name.replace ('-', '_'), oi);
585          if (oi.aliases.length > 0) {
586            for (String alias : oi.aliases) {
587              if (name_map.containsKey (alias))
588                throw new Error ("alias " + oi + " appears twice");
589              name_map.put (alias, oi);
590            }
591          }
592        }
593      }
594    
595      /**
596       * Like getAnnotation, but returns null (and prints a warning) rather
597       * than throwing an exception.
598       */
599      private <T extends Annotation> /*@Nullable*/ T
600      safeGetAnnotation(Field f, Class<T> annotationClass) {
601        /*@Nullable*/ T annotation;
602        try {
603          annotation = f.getAnnotation(annotationClass);
604        } catch (Exception e) {
605          // Can get
606          //   java.lang.ArrayStoreException: sun.reflect.annotation.TypeNotPresentExceptionProxy
607          // when an annotation is not present at run time (example: @NonNull)
608          System.out.printf("Exception in call to f.getAnnotation(%s)%n  for f=%s%n"
609                            + "  %s%nClasspath =%n", annotationClass, f, e.getMessage());
610          //e.printStackTrace();
611          JWhich.printClasspath();
612          annotation = null;
613        }
614    
615        return annotation;
616      }
617    
618      /**
619       * If true, Options will parse arguments even after a non-option
620       * command-line argument.  Setting this to true is useful to permit users
621       * to write options at the end of a command line.  Setting this to false
622       * is useful to avoid processing arguments that are actually
623       * options/arguments for another program that this one will invoke.
624       */
625      public void parse_options_after_arg (boolean val) {
626        parse_options_after_arg = val;
627      }
628    
629      /** @deprecated Use {@link #parse_options_after_arg(boolean)}. */
630      @Deprecated
631      public void ignore_options_after_arg (boolean val) {
632        parse_options_after_arg = !val;
633      }
634    
635      /**
636       * If true, long options (those derived from field names) will be parsed with
637       * a single dash prefix as in -longOption.  The default is false and long
638       * options will be parsed with a double dash prefix as in --longOption.
639       */
640      public void use_single_dash (boolean val) {
641        use_single_dash = val;
642      }
643    
644      /**
645       * Parses a command line and sets the options accordingly.
646       * @return all non-option arguments
647       * @throws ArgException if the command line contains unknown option or
648       * misused options.
649       */
650      public String[] parse (String[] args) throws ArgException {
651    
652        List<String> non_options = new ArrayList<String>();
653        // If true, then "--" has been seen and any argument starting with "-"
654        // is processed as an ordinary argument, not as an option.
655        boolean ignore_options = false;
656    
657        // Loop through each argument
658        for (int ii = 0; ii < args.length; ii++) {
659          String arg = args[ii];
660          if (arg.equals ("--")) {
661            ignore_options = true;
662          } else if ((arg.startsWith ("--") || arg.startsWith("-")) && !ignore_options) {
663            String arg_name;
664            String arg_value;
665            int eq_pos = arg.indexOf ('=');
666            if (eq_pos == -1) {
667              arg_name = arg;
668              arg_value = null;
669            } else {
670              arg_name = arg.substring (0, eq_pos);
671              arg_value = arg.substring (eq_pos+1);
672            }
673            OptionInfo oi = name_map.get (arg_name);
674            if (oi == null) {
675              StringBuilder msg = new StringBuilder();
676              msg.append(String.format("unknown option name '%s' in arg '%s'",
677                                       arg_name, arg));
678              if (false) { // for debugging
679                msg.append("; known options:");
680                for (String option_name : UtilMDE.sortedKeySet(name_map)) {
681                  msg.append(" ");
682                  msg.append(option_name);
683                }
684              }
685              throw new ArgException (msg.toString());
686            }
687            if (oi.argument_required() && (arg_value == null)) {
688              ii++;
689              if (ii >= args.length)
690                throw new ArgException ("option %s requires an argument", arg);
691              arg_value = args[ii];
692            }
693            // System.out.printf ("arg_name = '%s', arg_value='%s'%n", arg_name,
694            //                    arg_value);
695            set_arg (oi, arg_name, arg_value);
696          } else { // not an option
697            if (! parse_options_after_arg)
698              ignore_options = true;
699            non_options.add (arg);
700          }
701    
702        }
703        String[] result = non_options.toArray (new String[non_options.size()]);
704        return result;
705      }
706    
707      /**
708       * Parses a command line and sets the options accordingly.  This method
709       * splits the argument string into command line arguments, respecting
710       * single and double quotes, then calls parse(String[]).
711       * @return all non-option arguments
712       * @throws ArgException if the command line contains unknown option or
713       * misused options.
714       * @see #parse(String[])
715       */
716      public String[] parse (String args) throws ArgException {
717    
718        // Split the args string on whitespace boundaries accounting for quoted
719        // strings.
720        args = args.trim();
721        List<String> arg_list = new ArrayList<String>();
722        String arg = "";
723        char active_quote = 0;
724        for (int ii = 0; ii < args.length(); ii++) {
725          char ch = args.charAt (ii);
726          if ((ch == '\'') || (ch == '"')) {
727            arg+= ch;
728            ii++;
729            while ((ii < args.length()) && (args.charAt(ii) != ch))
730              arg += args.charAt(ii++);
731            arg += ch;
732          } else if (Character.isWhitespace (ch)) {
733            // System.out.printf ("adding argument '%s'%n", arg);
734            arg_list.add (arg);
735            arg = "";
736            while ((ii < args.length()) && Character.isWhitespace(args.charAt(ii)))
737              ii++;
738            if (ii < args.length())
739              ii--;
740          } else { // must be part of current argument
741            arg += ch;
742          }
743        }
744        if (!arg.equals (""))
745          arg_list.add (arg);
746    
747        String[] argsArray = arg_list.toArray (new String[arg_list.size()]);
748        return parse (argsArray);
749      }
750    
751      /**
752       * Parses a command line and sets the options accordingly.  If an error
753       * occurs, prints the usage and terminates the program.  The program is
754       * terminated rather than throwing an error to create cleaner output.
755       * @return all non-option arguments
756       * @see #parse(String[])
757       */
758      public String[] parse_or_usage (String[] args) {
759    
760        String non_options[] = null;
761    
762        try {
763          non_options = parse (args);
764        } catch (ArgException ae) {
765          String message = ae.getMessage();
766          if (message != null) {
767            print_usage (message);
768          } else {
769            print_usage ();
770          }
771          System.exit (-1);
772          // throw new Error ("usage error: ", ae);
773        }
774        return (non_options);
775      }
776    
777      /**
778       * Parses a command line and sets the options accordingly.  If an error
779       * occurs, prints the usage and terminates the program.  The program is
780       * terminated rather than throwing an error to create cleaner output.
781       * This method splits the argument string into command line arguments,
782       * respecting single and double quotes, then calls parse_or_usage(String[]).
783       * @return all non-option arguments
784       * @see #parse_or_usage(String[])
785       */
786      public String[] parse_or_usage (String args) {
787    
788        String non_options[] = null;
789    
790        try {
791          non_options = parse (args);
792        } catch (ArgException ae) {
793          String message = ae.getMessage();
794          if (message != null) {
795            print_usage (message);
796          } else {
797            print_usage ();
798          }
799          System.exit (-1);
800          // throw new Error ("usage error: ", ae);
801        }
802        return (non_options);
803      }
804    
805      /** @deprecated Use {@link #parse_or_usage(String[])}. */
806      @Deprecated
807      public String[] parse_and_usage (String[] args) {
808        return parse_or_usage(args);
809      }
810    
811      /** @deprecated Use {@link #parse_or_usage(String)}. */
812      @Deprecated
813      public String[] parse_and_usage (String args) {
814        return parse_or_usage(args);
815      }
816    
817    
818      /// This is a lot of methods, but it does save a tad of typing for the
819      /// programmer.
820    
821      /**
822       * Prints usage information.  Uses the usage synopsis passed into the
823       * constructor, if any.
824       */
825      public void print_usage (PrintStream ps) {
826        if (usage_synopsis != null) {
827          ps.printf ("Usage: %s%n", usage_synopsis);
828        }
829        ps.println(usage());
830      }
831    
832      /**
833       * Prints, to standard output, usage information.
834       */
835      public void print_usage () {
836        print_usage (System.out);
837      }
838    
839      // This method is distinct from
840      //   print_usage (PrintStream ps, String format, Object... args)
841      // because % characters in the message are not interpreted.
842      /**
843       * Prints a message followed by indented usage information.
844       * The message is printed in addition to (not replacing) the usage synopsis.
845       **/
846      public void print_usage (PrintStream ps, String msg) {
847        ps.println (msg);
848        print_usage (ps);
849      }
850    
851      /**
852       * Prints, to standard output, a message followed by usage information.
853       * The message is printed in addition to (not replacing) the usage synopsis.
854       **/
855      public void print_usage (String msg) {
856        print_usage (System.out, msg);
857      }
858    
859      /**
860       * Prints a message followed by usage information.
861       * The message is printed in addition to (not replacing) the usage synopsis.
862       */
863      public void print_usage (PrintStream ps, String format, /*@Nullable*/ Object... args) {
864        ps.printf (format, args);
865        if (! format.endsWith("%n")) {
866          ps.println();
867        }
868        print_usage (ps);
869      }
870    
871      /**
872       * Prints, to standard output, a message followed by usage information.
873       * The message is printed in addition to (not replacing) the usage synopsis.
874       */
875      public void print_usage (String format, /*@Nullable*/ Object... args) {
876        print_usage(System.out, format, args);
877      }
878    
879      /**
880       * Returns the String containing the usage message for command-line options.
881       * @param group_names The list of option groups to include in the usage
882       * message.  If empty, will return usage for all option groups which are not
883       * unpublicized.  Or if option groups are not being used, will return usage
884       * for all options which are not unpublicized.
885       */
886      public String usage(String... group_names) {
887        if (!use_groups) {
888          if (group_names.length > 0) {
889            throw new IllegalArgumentException(
890              "This instance of Options does not have any option groups defined");
891          }
892          return format_options(options, max_opt_len(options));
893        }
894         
895        List<OptionGroupInfo> groups = new ArrayList<OptionGroupInfo>();
896        if (group_names.length > 0) {
897          for (String group_name : group_names) {
898            if (!group_map.containsKey(group_name))
899              throw new IllegalArgumentException("invalid option group: " + group_name);
900            else
901              groups.add(group_map.get(group_name));
902          }
903        } else { // return usage for all groups which are not unpublicized
904          for (OptionGroupInfo gi : group_map.values()) {
905            if (gi.unpublicized)
906              continue;
907            groups.add(gi);
908          }
909        }
910    
911        List<Integer> lengths = new ArrayList<Integer>();
912        for (OptionGroupInfo gi : groups)
913          lengths.add(max_opt_len(gi.optionList));
914        int max_len = Collections.max(lengths);
915    
916        StringBuilderDelimited buf = new StringBuilderDelimited(eol);
917        for (OptionGroupInfo gi : groups) {
918          buf.append(String.format("%n%s:", gi.name));
919          buf.append(format_options(gi.optionList, max_len));
920        }
921    
922        return buf.toString();
923      }
924    
925      /**
926       * Entry point for creating HTML documentation.  HTML documentation includes
927       * unpublicized option groups but not <code>@Unpublicized</code> options.
928       */
929      public void jdoc (RootDoc doc) {
930        // Find the overall documentation (on the main class)
931        ClassDoc main = find_class_doc (doc, main_class);
932        if (main == null) {
933          throw new Error ("can't find main class " + main_class);
934        }
935    
936        // Process each option and add in the javadoc info
937        for (OptionInfo oi : options) {
938          ClassDoc opt_doc = find_class_doc (doc, oi.get_declaring_class());
939          String nameWithUnderscores = oi.long_name.replace('-', '_');
940          if (opt_doc != null) {
941            for (FieldDoc fd : opt_doc.fields()) {
942              if (fd.name().equals (nameWithUnderscores)) {
943                oi.jdoc = format_comment(fd);
944                break;
945              }
946            }
947          }
948        }
949    
950        // Write out the info as HTML
951        System.out.println (format_comment(main));
952        System.out.println ("<p>Command line options: </p>");
953        System.out.println ("<ul>");
954    
955        if (!use_groups)
956          System.out.println(format_options_html(options, 2));
957        else {
958          for (OptionGroupInfo gi : group_map.values()) {
959            System.out.println("  <li>" + gi.name);
960            System.out.println("    <ul>");
961            System.out.println(format_options_html(gi.optionList, 6));
962            System.out.println("    </ul>");
963            System.out.println("  </li>");
964          }
965        }
966        System.out.println ("</ul>");
967      }
968    
969      /*@Nullable*/ ClassDoc find_class_doc (RootDoc doc, Class<?> c) {
970    
971        for (ClassDoc cd : doc.classes()) {
972          if (cd.qualifiedName().equals (c.getName())) {
973            return cd;
974          }
975        }
976        return (null);
977      }
978    
979      /**
980       * Format a javadoc comment to HTML by wrapping the text of inline @link tags
981       * and block @see tags in HTML 'code' tags.  This keeps most of the
982       * information in the comment while still being presentable. <p>
983       * 
984       * This is only a temporary solution.  Ideally, a custom doclet (perhaps
985       * subclassing HtmlDoclet) would be created which integrates command-line
986       * option documentation with the rest of the javadoc documentation for a
987       * project.
988       */
989      private String format_comment(Doc doc) {
990        StringBuilder buf = new StringBuilder();
991        Tag[] tags = doc.inlineTags();
992        for (Tag tag : tags) {
993          if (tag instanceof SeeTag)
994            buf.append("<code>" + tag.text() + "</code>");
995          else
996            buf.append(tag.text());
997        }
998        SeeTag[] seetags = doc.seeTags();
999        if (seetags.length > 0) {
1000          buf.append(" See: ");
1001          StringBuilderDelimited seebuf = new StringBuilderDelimited(", ");
1002          for (SeeTag tag : seetags)
1003            seebuf.append("<code>" + tag.text() + "</code>");
1004          buf.append(seebuf);
1005          buf.append(".");
1006        }
1007        return buf.toString();
1008      }
1009    
1010      /**
1011       * Format a list of options for use in generating usage messages.
1012       */
1013      private String format_options(List<OptionInfo> opt_list, int max_len) {
1014        StringBuilderDelimited buf = new StringBuilderDelimited(eol);
1015        for (OptionInfo oi : opt_list) {
1016          if (oi.unpublicized)
1017            continue;
1018          String default_str = "[no default]";
1019          if (oi.default_str != null)
1020            default_str = String.format("[default %s]", oi.default_str);
1021          String use = String.format("  %-" + max_len + "s - %s %s",
1022                                     oi.synopsis(), oi.description, default_str);
1023          buf.append(use);
1024        }
1025        return buf.toString();
1026      }
1027    
1028      /**
1029       * Format a list of options with HTML for use in generating the HTML
1030       * documentation.
1031       */
1032      private String format_options_html(List<OptionInfo> opt_list, int indent) {
1033        StringBuilderDelimited buf = new StringBuilderDelimited(eol);
1034        for (OptionInfo oi : opt_list) {
1035          if (oi.unpublicized)
1036            continue;
1037          String default_str = "[no default]";
1038          if (oi.default_str != null)
1039            default_str = String.format ("[default %s]", oi.default_str);
1040          String synopsis = oi.synopsis();
1041          synopsis = synopsis.replaceAll ("<", "&lt;");
1042          synopsis = synopsis.replaceAll (">", "&gt;");
1043          String alias_str = "";
1044          if (oi.aliases.length > 0) {
1045            Iterator<String> it = Arrays.asList(oi.aliases).iterator();
1046            StringBuilderDelimited b = new StringBuilderDelimited(", ");
1047            while (it.hasNext())
1048                b.append(String.format("<b>%s</b>", it.next()));
1049            alias_str = "<i>Aliases</i>: " + b.toString() + ". ";
1050          }
1051          buf.append(String.format("%" + indent + "s<li> <b>%s</b>. %s %s%s</li>",
1052                     "", synopsis, oi.jdoc, alias_str, default_str));
1053        }
1054        return buf.toString();
1055      }
1056    
1057      private int max_opt_len(List<OptionInfo> opt_list) {
1058        int max_len = 0;
1059        for (OptionInfo oi : opt_list) {
1060          if (oi.unpublicized)
1061            continue;
1062          int len = oi.synopsis().length();
1063          if (len > max_len)
1064            max_len = len;
1065        }
1066        return max_len;
1067      }
1068    
1069      /**
1070       * Set the specified option to the value specified in arg_value.  Throws
1071       * an ArgException if there are any errors.
1072       */
1073      private void set_arg (OptionInfo oi, String arg_name, /*@Nullable*/ String arg_value)
1074        throws ArgException {
1075    
1076        Field f = oi.field;
1077        Class<?> type = oi.base_type;
1078    
1079        // Keep track of all of the options specified
1080        if (options_str.length() > 0)
1081          options_str += " ";
1082        options_str += arg_name;
1083        if (arg_value != null) {
1084          if (! arg_value.contains (" ")) {
1085            options_str += "=" + arg_value;
1086          } else if (! arg_value.contains ("'")) {
1087            options_str += "='" + arg_value + "'";
1088          } else if (! arg_value.contains ("\"")) {
1089            options_str += "=\"" + arg_value + "\"";
1090          } else {
1091            throw new ArgException("Can't quote for interal debugging: " + arg_value);
1092          }
1093        }
1094        // Argument values are required for everything but booleans
1095        if (arg_value == null) {
1096          if ((type != Boolean.TYPE)
1097              || (type != Boolean.class)) {
1098            arg_value = "true";
1099          } else {
1100            throw new ArgException ("Value required for option " + arg_name);
1101          }
1102        }
1103    
1104        try {
1105          if (type.isPrimitive()) {
1106            if (type == Boolean.TYPE) {
1107              boolean val;
1108              String arg_value_lowercase = arg_value.toLowerCase();
1109              if (arg_value_lowercase.equals ("true") || (arg_value_lowercase.equals ("t")))
1110                val = true;
1111              else if (arg_value_lowercase.equals ("false") || arg_value_lowercase.equals ("f"))
1112                val = false;
1113              else
1114                throw new ArgException ("Value \"%s\" for argument %s is not a boolean",
1115                                        arg_value, arg_name);
1116              arg_value = (val) ? "true" : "false";
1117              // System.out.printf ("Setting %s to %s%n", arg_name, val);
1118              f.setBoolean (oi.obj, val);
1119            } else if (type == Integer.TYPE) {
1120              int val;
1121              try {
1122                val = Integer.decode (arg_value);
1123              } catch (Exception e) {
1124                throw new ArgException ("Value \"%s\" for argument %s is not an integer",
1125                                        arg_value, arg_name);
1126              }
1127              f.setInt (oi.obj, val);
1128            } else if (type == Long.TYPE) {
1129              long val;
1130              try {
1131                val = Long.decode (arg_value);
1132              } catch (Exception e) {
1133                throw new ArgException ("Value \"%s\" for argument %s is not a long integer",
1134                                        arg_value, arg_name);
1135              }
1136              f.setLong (oi.obj, val);
1137            } else if (type == Float.TYPE) {
1138              Float val;
1139              try {
1140                val = Float.valueOf (arg_value);
1141              } catch (Exception e) {
1142                throw new ArgException ("Value \"%s\" for argument %s is not a float",
1143                                        arg_value, arg_name);
1144              }
1145              f.setFloat (oi.obj, val);
1146            } else if (type == Double.TYPE) {
1147              Double val;
1148              try {
1149                val = Double.valueOf (arg_value);
1150              } catch (Exception e) {
1151                throw new ArgException ("Value \"%s\" for argument %s is not a double",
1152                                        arg_value, arg_name);
1153              }
1154              f.setDouble (oi.obj, val);
1155            } else { // unexpected type
1156              throw new Error ("Unexpected type " + type);
1157            }
1158          } else { // reference type
1159    
1160            // If the argument is a list, add repeated arguments or multiple
1161            // blank separated arguments to the list, otherwise just set the
1162            // argument value.
1163            if (oi.list != null) {
1164              if (split_lists) {
1165                String[] aarr = arg_value.split ("  *");
1166                for (String aval : aarr) {
1167                  Object val = get_ref_arg (oi, arg_name, aval);
1168                  oi.list.add (val); // uncheck cast
1169                }
1170              } else {
1171                Object val = get_ref_arg (oi, arg_name, arg_value);
1172                oi.list.add (val);
1173              }
1174            } else {
1175              Object val = get_ref_arg (oi, arg_name, arg_value);
1176              f.set (oi.obj, val);
1177            }
1178          }
1179        } catch (ArgException ae) {
1180          throw ae;
1181        } catch (Exception e) {
1182          throw new Error ("Unexpected error ", e);
1183        }
1184      }
1185    
1186      /**
1187       * Create an instance of the correct type by passing the argument value
1188       * string to the constructor.  The only expected error is some sort
1189       * of parse error from the constructor.
1190       */
1191      private /*@NonNull*/ Object get_ref_arg (OptionInfo oi, String arg_name, 
1192                                               String arg_value) throws ArgException {
1193    
1194        Object val = null;
1195        try {
1196          if (oi.constructor != null) {
1197            val = oi.constructor.newInstance (arg_value);
1198          } else if (oi.base_type.isEnum()) {
1199            @SuppressWarnings({"unchecked","rawtypes"})
1200            Object tmpVal = Enum.valueOf ((Class<? extends Enum>)oi.base_type,
1201                                           arg_value);
1202            val = tmpVal;
1203          } else {
1204            if (oi.factory == null) {
1205              throw new Error("No constructor or factory for argument " + arg_name);
1206            }
1207            val = oi.factory.invoke (null, arg_value);
1208          }
1209        } catch (Exception e) {
1210          throw new ArgException ("Invalid argument (%s) for argument %s",
1211                                  arg_value, arg_name);
1212        }
1213    
1214        assert val != null : "@SuppressWarnings(nullness)";
1215        return val;
1216      }
1217    
1218      /**
1219       * Returns a short name for the specified type for use in messages.
1220       */
1221      private static String type_short_name (Class<?> type) {
1222    
1223        if (type.isPrimitive())
1224          return type.getName();
1225        else if (type == File.class)
1226          return "filename";
1227        else if (type == Pattern.class)
1228          return "regex";
1229        else if (type.isEnum())
1230          return ("enum");
1231        else
1232          return UtilMDE.unqualified_name (type.getName()).toLowerCase();
1233      }
1234    
1235      /**
1236       * Returns a string containing all of the options that were set and their
1237       * arguments.  This is essentially the contents of args[] with all
1238       * non-options removed.
1239       * @see #settings()
1240       */
1241      public String get_options_str() {
1242        return (options_str);
1243      }
1244    
1245      /**
1246       * Returns a string containing the current setting for each option, in a
1247       * format that can be parsed by Options.  This differs from
1248       * get_options_str() in that it contains each known option exactly once:
1249       * it never contains duplicates, and it contains every known option even
1250       * if the option was not specified on the command line.
1251       */
1252      public String settings () {
1253        StringBuilderDelimited out = new StringBuilderDelimited(eol);
1254    
1255        // Determine the length of the longest name
1256        int max_len = max_opt_len(options);
1257    
1258        // Create the settings string
1259        for (OptionInfo oi : options) {
1260          String use = String.format ("%-" + max_len + "s = ", oi.long_name);
1261          try {
1262            use += oi.field.get (oi.obj);
1263          } catch (Exception e) {
1264            throw new Error ("unexpected exception reading field " + oi.field, e);
1265          }
1266          out.append(use);
1267        }
1268    
1269        return out.toString();
1270      }
1271    
1272      /**
1273       * Returns a description of all of the known options.
1274       * Each option is described on its own line in the output.
1275       */
1276      public String toString() {
1277        StringBuilderDelimited out = new StringBuilderDelimited(eol);
1278    
1279        for (OptionInfo oi: options) {
1280          out.append(oi);
1281        }
1282    
1283        return out.toString();
1284      }
1285    
1286      /**
1287       * Exceptions encountered during argument processing.
1288       */
1289      public static class ArgException extends Exception {
1290        static final long serialVersionUID = 20051223L;
1291        public ArgException (String s) { super (s); }
1292        public ArgException (String format, /*@Nullable*/ Object... args) {
1293          super (String.format (format, args));
1294        }
1295      }
1296    
1297    
1298      private static class ParseResult {
1299        /*@Nullable*/ String short_name;
1300        /*@Nullable*/ String type_name;
1301        String description;
1302        ParseResult(/*@Nullable*/ String short_name, /*@Nullable*/ String type_name, String description) {
1303          this.short_name = short_name;
1304          this.type_name = type_name;
1305          this.description = description;
1306        }
1307      }
1308    
1309    
1310      /**
1311       * Parse an option value and return its three components (short_name,
1312       * type_name, and description).  The short_name and type_name are null
1313       * if they are not specified in the string.
1314       */
1315      private static ParseResult parse_option (String val) {
1316    
1317        // Get the short name, long name, and description
1318        String short_name;
1319        String type_name;
1320        /*@NonNull*/ String description;
1321    
1322        // Get the short name (if any)
1323        if (val.startsWith("-")) {
1324          assert val.substring(2,3).equals(" ");
1325          short_name = val.substring (1, 2);
1326          description = val.substring (3);
1327        } else {
1328          short_name = null;
1329          description = val;
1330        }
1331    
1332        // Get the type name (if any)
1333        if (description.startsWith ("<")) {
1334          type_name = description.substring (1).replaceFirst (">.*", "");
1335          description = description.replaceFirst ("<.*> ", "");
1336        } else {
1337          type_name = null;
1338        }
1339    
1340        // Return the result
1341        return new ParseResult(short_name, type_name, description);
1342      }
1343    
1344    //   /**
1345    //    * Test class with some defined arguments.
1346    //    */
1347    //   private static class Test {
1348    //
1349    //     @Option ("generic") List<Pattern> lp = new ArrayList<Pattern>();
1350    //     @Option ("-a <filename> argument 1") String arg1 = "/tmp/foobar";
1351    //     @Option ("argument 2") String arg2;
1352    //     @Option ("-d double value") double temperature;
1353    //     @Option ("-f the input file") File input_file;
1354    //   }
1355    //
1356    //   /**
1357    //    * Simple example
1358    //    */
1359    //   private static void main (String[] args) throws ArgException {
1360    //
1361    //     Options options = new Options ("test", new Test());
1362    //     System.out.printf ("Options:%n%s", options);
1363    //     options.parse_or_usage (args);
1364    //     System.out.printf ("Results:%n%s", options.settings());
1365    //   }
1366    
1367    }