
io.reactiverse.es4x.cli.CmdLineParser Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2001-2012 Steve Purcell.
* Copyright (c) 2002 Vidar Holen.
* Copyright (c) 2002 Michal Ceresna.
* Copyright (c) 2005 Ewan Mellor.
* Copyright (c) 2010-2012 penSec.IT UG (haftungsbeschränkt).
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. Redistributions in binary
* form must reproduce the above copyright notice, this list of conditions and
* the following disclaimer in the documentation and/or other materials provided
* with the distribution. Neither the name of the copyright holder nor the names
* of its contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package io.reactiverse.es4x.cli;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/**
* Largely GNU-compatible command-line options parser. Has short (-v) and
* long-form (--verbose) option support, and also allows options with
* associated values (-d 2, --debug 2, --debug=2). Option processing
* can be explicitly terminated by the argument '--'.
*
* @author Steve Purcell
* @author penSec.IT UG (haftungsbeschränkt)
* @author Paulo Lopes
*
* @version 2.0
*/
public class CmdLineParser {
/**
* Base class for exceptions that may be thrown when options are parsed
*/
public static abstract class OptionException extends Exception {
OptionException(String msg) { super(msg); }
}
/**
* Thrown when the parsed command-line contains an option that is not
* recognised. {@code getMessage()} returns
* an error string suitable for reporting the error to the user (in
* English).
*/
public static class UnknownOptionException extends OptionException {
UnknownOptionException( String optionName ) {
this(optionName, "Unknown option '" + optionName + "'");
}
UnknownOptionException( String optionName, String msg ) {
super(msg);
this.optionName = optionName;
}
/**
* @return the name of the option that was unknown (e.g. "-u")
*/
public String getOptionName() {
return this.optionName;
}
private final String optionName;
}
/**
* Thrown when the parsed commandline contains multiple concatenated
* short options, such as -abcd, where one is unknown.
* {@code getMessage()} returns an english human-readable error
* string.
* @author Vidar Holen
*/
public static class UnknownSuboptionException
extends UnknownOptionException {
private final char suboption;
UnknownSuboptionException( String option, char suboption ) {
super(option, "Illegal option: '"+suboption+"' in '"+option+"'");
this.suboption=suboption;
}
public char getSuboption() {
return suboption;
}
}
/**
* Thrown when the parsed commandline contains multiple concatenated
* short options, such as -abcd, where one or more requires a value.
* {@code getMessage()} returns an english human-readable error
* string.
* @author Vidar Holen
*/
public static class NotFlagException extends UnknownOptionException {
private final char notflag;
NotFlagException( String option, char unflaggish ) {
super(option, "Illegal option: '"+option+"', '"+
unflaggish+"' requires a value");
notflag=unflaggish;
}
/**
* @return the first character which wasn't a boolean (e.g 'c')
*/
public char getOptionChar() {
return notflag;
}
}
/**
* Thrown when an illegal or missing value is given by the user for
* an option that takes a value. {@code getMessage()} returns
* an error string suitable for reporting the error to the user (in
* English).
*
* No generic class can ever extend {@code java.lang.Throwable}, so we
* have to return {@code Option>} instead of
* {@code Option}.
*/
public static class IllegalOptionValueException extends OptionException {
public IllegalOptionValueException( Option opt, String value ) {
super("Illegal value '" + value + "' for option " +
(opt.shortForm() != null ? "-" + opt.shortForm() + "/" : "") +
"--" + opt.longForm());
this.option = opt;
this.value = value;
}
/**
* @return the name of the option whose value was illegal (e.g. "-u")
*/
public Option> getOption() {
return this.option;
}
/**
* @return the illegal value
*/
public String getValue() {
return this.value;
}
private final Option> option;
private final String value;
}
/**
* Representation of a command-line option
*
* @param Type of data configured by this option
*/
public static abstract class Option {
protected Option( String longForm, boolean wantsValue ) {
this(null, longForm, wantsValue);
}
protected Option( char shortForm, String longForm,
boolean wantsValue ) {
this(new String(new char[]{shortForm}), longForm, wantsValue);
}
private Option( String shortForm, String longForm, boolean wantsValue ) {
if ( longForm == null ) {
throw new IllegalArgumentException("Null longForm not allowed");
}
this.shortForm = shortForm;
this.longForm = longForm;
this.wantsValue = wantsValue;
}
public String shortForm() {
return this.shortForm;
}
public String longForm() {
return this.longForm;
}
/**
* Tells whether or not this option wants a value
* @return true if the options wants a value
*/
public boolean wantsValue() {
return this.wantsValue;
}
public final T getValue( String arg, Locale locale )
throws IllegalOptionValueException {
if ( this.wantsValue ) {
if ( arg == null ) {
throw new IllegalOptionValueException(this, "");
}
return this.parseValue(arg, locale);
} else {
return this.getDefaultValue();
}
}
/**
* Override to extract and convert an option value passed on the
* command-line
* @param arg the value
* @param locale the locale used to parse
* @return the parsed value
* @throws IllegalOptionValueException if cannot parse
*/
protected T parseValue(String arg, Locale locale)
throws IllegalOptionValueException {
return null;
}
/**
* Override to define default value returned by getValue if option does
* not want a value
* @return the default value for the option (null)
*/
protected T getDefaultValue() {
return null;
}
private final String shortForm;
private final String longForm;
private final boolean wantsValue;
/**
* An option that expects a boolean value
*/
public static class BooleanOption extends Option {
public BooleanOption( char shortForm, String longForm ) {
super(shortForm, longForm, false);
}
public BooleanOption( String longForm ) {
super(longForm, false);
}
@Override
public Boolean parseValue(String arg, Locale lcoale) {
return Boolean.TRUE;
}
@Override
public Boolean getDefaultValue() {
return Boolean.TRUE;
}
}
/**
* An option that expects an integer value
*/
public static class IntegerOption extends Option {
public IntegerOption( char shortForm, String longForm ) {
super(shortForm, longForm, true);
}
public IntegerOption( String longForm ) {
super(longForm, true);
}
@Override
protected Integer parseValue( String arg, Locale locale )
throws IllegalOptionValueException {
try {
return Integer.decode(arg);
} catch (NumberFormatException e) {
throw new IllegalOptionValueException(this, arg);
}
}
}
/**
* An option that expects a long integer value
*/
public static class LongOption extends Option {
public LongOption( char shortForm, String longForm ) {
super(shortForm, longForm, true);
}
public LongOption( String longForm ) {
super(longForm, true);
}
@Override
protected Long parseValue( String arg, Locale locale )
throws IllegalOptionValueException {
try {
return Long.decode(arg);
} catch (NumberFormatException e) {
throw new IllegalOptionValueException(this, arg);
}
}
}
/**
* An option that expects a floating-point value
*/
public static class DoubleOption extends Option {
public DoubleOption( char shortForm, String longForm ) {
super(shortForm, longForm, true);
}
public DoubleOption( String longForm ) {
super(longForm, true);
}
@Override
protected Double parseValue( String arg, Locale locale )
throws IllegalOptionValueException {
try {
NumberFormat format = NumberFormat.getNumberInstance(locale);
Number num = format.parse(arg);
return num.doubleValue();
} catch (ParseException e) {
throw new IllegalOptionValueException(this, arg);
}
}
}
/**
* An option that expects a string value
*/
public static class StringOption extends Option {
public StringOption( char shortForm, String longForm ) {
super(shortForm, longForm, true);
}
public StringOption( String longForm ) {
super(longForm, true);
}
@Override
protected String parseValue( String arg, Locale locale ) {
return arg;
}
}
}
/**
* Add the specified Option to the list of accepted options
* @param opt the option to add
* @param the kind of option
* @return the opt param
*/
public final Option addOption( Option opt ) {
if ( opt.shortForm() != null ) {
this.options.put("-" + opt.shortForm(), opt);
}
this.options.put("--" + opt.longForm(), opt);
return opt;
}
/**
* Convenience method for adding a string option.
* @param shortForm the short form character
* @param longForm the long form string
* @return the new Option
*/
public final Option addStringOption( char shortForm, String longForm ) {
return addOption(new Option.StringOption(shortForm, longForm));
}
/**
* Convenience method for adding a string option.
* @param longForm the long form string
* @return the new Option
*/
public final Option addStringOption( String longForm ) {
return addOption(new Option.StringOption(longForm));
}
/**
* Convenience method for adding an integer option.
* @param shortForm the short form character
* @param longForm the long form string
* @return the new Option
*/
public final Option addIntegerOption( char shortForm, String longForm ) {
return addOption(new Option.IntegerOption(shortForm, longForm));
}
/**
* Convenience method for adding an integer option.
* @param longForm the long form string
* @return the new Option
*/
public final Option addIntegerOption( String longForm ) {
return addOption(new Option.IntegerOption(longForm));
}
/**
* Convenience method for adding a long integer option.
* @param shortForm the short form character
* @param longForm the long form string
* @return the new Option
*/
public final Option addLongOption( char shortForm, String longForm ) {
return addOption(new Option.LongOption(shortForm, longForm));
}
/**
* Convenience method for adding a long integer option.
* @param longForm the long form string
* @return the new Option
*/
public final Option addLongOption( String longForm ) {
return addOption(new Option.LongOption(longForm));
}
/**
* Convenience method for adding a double option.
* @param shortForm the short form character
* @param longForm the long form string
* @return the new Option
*/
public final Option addDoubleOption( char shortForm, String longForm ) {
return addOption(new Option.DoubleOption(shortForm, longForm));
}
/**
* Convenience method for adding a double option.
* @param longForm the long form string
* @return the new Option
*/
public final Option addDoubleOption( String longForm ) {
return addOption(new Option.DoubleOption(longForm));
}
/**
* Convenience method for adding a boolean option.
* @param shortForm the short form character
* @param longForm the long form string
* @return the new Option
*/
public final Option addBooleanOption( char shortForm, String longForm ) {
return addOption(new Option.BooleanOption(shortForm, longForm));
}
/**
* Convenience method for adding a boolean option.
* @param longForm the long form string
* @return the new Option
*/
public final Option addBooleanOption( String longForm ) {
return addOption(new Option.BooleanOption(longForm));
}
/**
* Equivalent to {@link #getOptionValue(Option, Object) getOptionValue(o,
* null)}.
* @param o the option to extract the value
* @param the kind of option
* @return the value or null
*/
public final T getOptionValue( Option o ) {
return getOptionValue(o, null);
}
/**
* @param o the option to extract the value
* @param def the default value if vallue is null or empty
* @param the kind of option
* @return the parsed value of the given Option, or the given default 'def'
* if the option was not set
*/
public final T getOptionValue( Option o, T def ) {
List> v = values.get(o.longForm());
if (v == null) {
return def;
} else if (v.isEmpty()) {
return null;
} else {
/* Cast should be safe because Option.parseValue has to return an
* instance of type T or null
*/
@SuppressWarnings("unchecked")
T result = (T)v.remove(0);
return result;
}
}
/**
* @param option the option to extract the value
* @param the kind of option
* @return A Collection giving the parsed values of all the occurrences of
* the given Option, or an empty Collection if the option was not set.
*/
public final Collection getOptionValues(Option option) {
Collection result = new ArrayList<>();
while (true) {
T o = getOptionValue(option, null);
if (o == null) {
return result;
} else {
result.add(o);
}
}
}
/**
* @return the non-option arguments
*/
public final String[] getRemainingArgs() {
return this.remainingArgs;
}
/**
* Extract the options and non-option arguments from the given
* list of command-line arguments. The default locale is used for
* parsing options whose values might be locale-specific.
*
* @param argv the command line arguments to parse
* @throws OptionException if cannot parse the options
*/
public final void parse( String[] argv ) throws OptionException {
parse(argv, Locale.getDefault());
}
/**
* Extract the options and non-option arguments from the given
* list of command-line arguments. The specified locale is used for
* parsing options whose values might be locale-specific.
*
* @param argv the command line arguments to parse
* @param locale the locale to use during the parsing
* @throws OptionException if cannot parse the options
*/
public final void parse( String[] argv, Locale locale )
throws OptionException {
ArrayList
© 2015 - 2025 Weber Informatics LLC | Privacy Policy