View Javadoc

1   package jargs.gnu;
2   
3   import java.text.NumberFormat;
4   import java.text.ParseException;
5   import java.util.Hashtable;
6   import java.util.Vector;
7   import java.util.Enumeration;
8   import java.util.Locale;
9   
10  /***
11   * Largely GNU-compatible command-line options parser. Has short (-v) and
12   * long-form (--verbose) option support, and also allows options with
13   * associated values (-d 2, --debug 2, --debug=2). Option processing
14   * can be explicitly terminated by the argument '--'.
15   *
16   * @author Steve Purcell
17   * @version Revision: 1.5
18   * @version $Id: CmdLineParser.java,v 1.1 2004/05/13 01:22:32 dquintela Exp $
19   * @see jargs.examples.gnu.OptionTest
20   */
21  public class CmdLineParser {
22  
23      /***
24       * Base class for exceptions that may be thrown when options are parsed
25       */
26      public static abstract class OptionException extends Exception {
27          OptionException(String msg) { super(msg); }
28      }
29  
30      /***
31       * Thrown when the parsed command-line contains an option that is not
32       * recognised. <code>getMessage()</code> returns
33       * an error string suitable for reporting the error to the user (in
34       * English).
35       */
36      public static class UnknownOptionException extends OptionException {
37          UnknownOptionException( String optionName ) {
38              super("unknown option '" + optionName + "'");
39              this.optionName = optionName;
40          }
41  
42          /***
43           * @return the name of the option that was unknown (e.g. "-u")
44           */
45          public String getOptionName() { return this.optionName; }
46          private String optionName = null;
47      }
48  
49      /***
50       * Thrown when an illegal or missing value is given by the user for
51       * an option that takes a value. <code>getMessage()</code> returns
52       * an error string suitable for reporting the error to the user (in
53       * English).
54       */
55      public static class IllegalOptionValueException extends OptionException {
56          public IllegalOptionValueException( Option opt, String value ) {
57              super("illegal value '" + value + "' for option -" +
58                      opt.shortForm() + "/--" + opt.longForm());
59              this.option = opt;
60              this.value = value;
61          }
62  
63          /***
64           * @return the name of the option whose value was illegal (e.g. "-u")
65           */
66          public Option getOption() { return this.option; }
67  
68          /***
69           * @return the illegal value
70           */
71          public String getValue() { return this.value; }
72          private Option option;
73          private String value;
74      }
75  
76      /***
77       * Representation of a command-line option
78       */
79      public static abstract class Option {
80  
81          protected Option( char shortForm, String longForm,
82                  boolean wantsValue ) {
83              if ( longForm == null )
84                  throw new IllegalArgumentException("null arg forms not allowed");
85              this.shortForm = new String(new char[]{shortForm});
86              this.longForm = longForm;
87              this.wantsValue = wantsValue;
88          }
89  
90          public String shortForm() { return this.shortForm; }
91  
92          public String longForm() { return this.longForm; }
93  
94          /***
95           * Tells whether or not this option wants a value
96           */
97          public boolean wantsValue() { return this.wantsValue; }
98  
99          public final Object getValue( String arg, Locale locale )
100         throws IllegalOptionValueException {
101             if ( this.wantsValue ) {
102                 if ( arg == null ) {
103                     throw new IllegalOptionValueException(this, "");
104                 }
105                 return this.parseValue(arg, locale);
106             }
107             else {
108                 return Boolean.TRUE;
109             }
110         }
111 
112         /***
113          * Override to extract and convert an option value passed on the
114          * command-line
115          */
116         protected Object parseValue( String arg, Locale locale )
117         throws IllegalOptionValueException {
118             return null;
119         }
120 
121         private String shortForm = null;
122         private String longForm = null;
123         private boolean wantsValue = false;
124 
125         public static class BooleanOption extends Option {
126             public BooleanOption( char shortForm, String longForm ) {
127                 super(shortForm, longForm, false);
128             }
129         }
130 
131         /***
132          * An option that expects an integer value
133          */
134         public static class IntegerOption extends Option {
135             public IntegerOption( char shortForm, String longForm ) {
136                 super(shortForm, longForm, true);
137             }
138             protected Object parseValue( String arg, Locale locale )
139             throws IllegalOptionValueException {
140                 try {
141                     return new Integer(arg);
142                 }
143                 catch (NumberFormatException e) {
144                     throw new IllegalOptionValueException(this, arg);
145                 }
146             }
147         }
148 
149         /***
150          * An option that expects a floating-point value
151          */
152         public static class DoubleOption extends Option {
153             public DoubleOption( char shortForm, String longForm ) {
154                 super(shortForm, longForm, true);
155             }
156             protected Object parseValue( String arg, Locale locale )
157             throws IllegalOptionValueException {
158                 try {
159                     NumberFormat format = NumberFormat.getNumberInstance(locale);
160                     Number num = (Number)format.parse(arg);
161                     return new Double(num.doubleValue());
162                 }
163                 catch (ParseException e) {
164                     throw new IllegalOptionValueException(this, arg);
165                 }
166             }
167         }
168 
169         /***
170          * An option that expects a string value
171          */
172         public static class StringOption extends Option {
173             public StringOption( char shortForm, String longForm ) {
174                 super(shortForm, longForm, true);
175             }
176             protected Object parseValue( String arg, Locale locale ) {
177                 return arg;
178             }
179         }
180     }
181 
182     /***
183      * Add the specified Option to the list of accepted options
184      */
185     public final Option addOption( Option opt ) {
186         this.options.put("-" + opt.shortForm(), opt);
187         this.options.put("--" + opt.longForm(), opt);
188         return opt;
189     }
190 
191     /***
192      * Convenience method for adding a string option.
193      * @return the new Option
194      */
195     public final Option addStringOption( char shortForm, String longForm ) {
196         Option opt = new Option.StringOption(shortForm, longForm);
197         addOption(opt);
198         return opt;
199     }
200 
201     /***
202      * Convenience method for adding an integer option.
203      * @return the new Option
204      */
205     public final Option addIntegerOption( char shortForm, String longForm ) {
206         Option opt = new Option.IntegerOption(shortForm, longForm);
207         addOption(opt);
208         return opt;
209     }
210 
211     /***
212      * Convenience method for adding a double option.
213      * @return the new Option
214      */
215     public final Option addDoubleOption( char shortForm, String longForm ) {
216         Option opt = new Option.DoubleOption(shortForm, longForm);
217         addOption(opt);
218         return opt;
219     }
220 
221     /***
222      * Convenience method for adding a boolean option.
223      * @return the new Option
224      */
225     public final Option addBooleanOption( char shortForm, String longForm ) {
226         Option opt = new Option.BooleanOption(shortForm, longForm);
227         addOption(opt);
228         return opt;
229     }
230 
231     /***
232      * @return the parsed value of the given Option, or null if the
233      * option was not set
234      */
235     public final Object getOptionValue( Option o ) {
236         return values.get(o.longForm());
237     }
238 
239     /***
240      * @return the non-option arguments
241      */
242     public final String[] getRemainingArgs() {
243         return this.remainingArgs;
244     }
245 
246     /***
247      * Extract the options and non-option arguments from the given
248      * list of command-line arguments. The default locale is used for
249      * parsing options whose values might be locale-specific.
250      */
251     public final void parse( String[] argv )
252     throws IllegalOptionValueException, UnknownOptionException {
253         parse(argv, Locale.getDefault());
254     }
255 
256     /***
257      * Extract the options and non-option arguments from the given
258      * list of command-line arguments. The specified locale is used for
259      * parsing options whose values might be locale-specific.
260      */
261     public final void parse( String[] argv, Locale locale )
262     throws IllegalOptionValueException, UnknownOptionException {
263         Vector otherArgs = new Vector();
264         int position = 0;
265         this.values = new Hashtable(10);
266         while ( position < argv.length ) {
267             String curArg = argv[position];
268             if ( curArg.startsWith("-") ) {
269                 if ( curArg.equals("--") ) { // end of options
270                     position += 1;
271                     break;
272                 }
273                 String valueArg = null;
274                 if ( curArg.startsWith("--") ) { // handle --arg=value
275                     int equalsPos = curArg.indexOf("=");
276                     if ( equalsPos != -1 ) {
277                         valueArg = curArg.substring(equalsPos+1);
278                         curArg = curArg.substring(0,equalsPos);
279                     }
280                 }
281                 Option opt = (Option)this.options.get(curArg);
282                 if ( opt == null ) {
283                     throw new UnknownOptionException(curArg);
284                 }
285                 Object value = null;
286                 if ( opt.wantsValue() ) {
287                     if ( valueArg == null ) {
288                         position += 1;
289                         valueArg = null;
290                         if ( position < argv.length ) {
291                             valueArg = argv[position];
292                         }
293                     }
294                     value = opt.getValue(valueArg, locale);
295                 }
296                 else {
297                     value = opt.getValue(null, locale);
298                 }
299                 this.values.put(opt.longForm(), value);
300                 position += 1;
301             }
302             else {
303                 break;
304             }
305         }
306         for ( ; position < argv.length; ++position ) {
307             otherArgs.addElement(argv[position]);
308         }
309 
310         this.remainingArgs = new String[otherArgs.size()];
311         int i = 0;
312         for (Enumeration e = otherArgs.elements(); e.hasMoreElements(); ++i) {
313             this.remainingArgs[i] = (String)e.nextElement();
314         }
315     }
316 
317     private String[] remainingArgs = null;
318     private Hashtable options = new Hashtable(10);
319     private Hashtable values = new Hashtable(10);
320 }