Mercurial > hg > truffle
diff graal/com.oracle.max.base/src/com/sun/max/program/option/OptionSet.java @ 3733:e233f5660da4
Added Java files from Maxine project.
author | Thomas Wuerthinger <thomas.wuerthinger@oracle.com> |
---|---|
date | Sat, 17 Dec 2011 19:59:18 +0100 |
parents | |
children | bc8527f3071c |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.max.base/src/com/sun/max/program/option/OptionSet.java Sat Dec 17 19:59:18 2011 +0100 @@ -0,0 +1,768 @@ +/* + * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.max.program.option; + +import java.io.*; +import java.net.*; +import java.util.*; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +import com.sun.max.*; +import com.sun.max.lang.*; +import com.sun.max.program.*; + +/** + * The {@code OptionSet} class parses and collects options from the command line and + * configuration files. + */ +public class OptionSet { + /** + * The {@code Syntax} enum allows different options to be parsed differently, + * depending on their usage. + */ + public enum Syntax { + REQUIRES_EQUALS { + @Override + public String getUsage(Option option) { + return "-" + option.getName() + "=" + option.getType().getValueFormat(); + } + }, + EQUALS_OR_BLANK { + @Override + public String getUsage(Option option) { + return "-" + option.getName() + "[=" + option.getType().getValueFormat() + "]"; + } + }, + REQUIRES_BLANK { + @Override + public String getUsage(Option option) { + return "-" + option.getName(); + } + }, + CONSUMES_NEXT { + @Override + public String getUsage(Option option) { + return "-" + option.getName() + " " + option.getType().getValueFormat(); + } + }; + + public abstract String getUsage(Option option); + } + + protected final Map<String, Option> optionMap; + protected final Map<String, Syntax> optionSyntax; + protected final Map<String, String> optionValues; + protected final boolean allowUnrecognizedOptions; + + protected static final String[] NO_ARGUMENTS = {}; + + protected String[] arguments = NO_ARGUMENTS; + + /** + * Creates an option set that does not allow unrecognized options to be present when + * {@linkplain #parseArguments(String[]) parsing command line arguments} or + * {@linkplain #loadOptions(OptionSet) loading options from another option set}. + */ + public OptionSet() { + this(false); + } + + /** + * Creates an option set. + * + * @param allowUnrecognizedOptions + * specifies if this option set allows unrecognized options to be present when + * {@linkplain #parseArguments(String[]) parsing command line arguments} or + * {@linkplain #loadOptions(OptionSet) loading options from another option set}. + */ + public OptionSet(boolean allowUnrecognizedOptions) { + optionValues = new HashMap<String, String>(); + // Using a LinkedHashMap to preserve insertion order when iterating over values + optionMap = new LinkedHashMap<String, Option>(); + optionSyntax = new HashMap<String, Syntax>(); + this.allowUnrecognizedOptions = allowUnrecognizedOptions; + } + + /** + * Converts this option set into a list of command line arguments, to be used, for example, to pass to an external + * tool. For each option in this set that has been explicitly set this method will prepend an appropriate option + * string of appropriate syntactic form (e.g. "-name=value") to the array of arguments passed. + * + * @return a new array of program arguments that includes these options + */ + public String[] asArguments() { + String[] newArgs = Arrays.copyOf(arguments, arguments.length + optionValues.size()); + int i = 0; + for (String name : optionValues.keySet()) { + final String value = optionValues.get(name); + final Syntax syntax = optionSyntax.get(name); + if (syntax == Syntax.REQUIRES_BLANK) { + newArgs[i++] = "-" + name; + } else if (syntax == Syntax.CONSUMES_NEXT) { + newArgs = Arrays.copyOf(newArgs, newArgs.length + 1); + newArgs[i++] = "-" + name; + newArgs[i++] = value; + } else { + newArgs[i++] = "-" + name + "=" + value; + } + } + return newArgs; + } + + /** + * Gets an option set derived from this option set that contains all the unrecognized options that have been loaded + * or parsed into this option set. The returned option set also includes a copy of the + * {@linkplain #getArguments() non-option arguments} from this option set. + * @return a new option set encapsulating all the arguments and options + */ + public OptionSet getArgumentsAndUnrecognizedOptions() { + final OptionSet argumentsAndUnrecognizedOptions = new OptionSet(true); + for (Map.Entry<String, String> entry : optionValues.entrySet()) { + if (!optionMap.containsKey(entry.getKey())) { + argumentsAndUnrecognizedOptions.optionValues.put(entry.getKey(), entry.getValue()); + } + } + argumentsAndUnrecognizedOptions.arguments = arguments; + return argumentsAndUnrecognizedOptions; + } + + /** + * Handles an Option.Error raised while loading or parsing values into this option set. + * <p> + * This default implementation is to print a usage message and the call {@link System#exit(int)}. + * @param error the error that occurred + * @param optionName the name of the option being parsed + */ + protected void handleErrorDuringParseOrLoad(Option.Error error, String optionName) { + System.out.println("Error parsing option -" + optionName + ": " + error.getMessage()); + printHelp(System.out, 78); + System.exit(1); + } + + /** + * Parses a list of command line arguments, processing the leading options (i.e. arguments that start with '-') + * and returning the "leftover" arguments to the caller. The longest tail of {@code arguments} that starts with a non-option argument can be retrieved after parsing with {@link #getArguments()}. + * + * @param args + * the arguments + * @return this option set + */ + public OptionSet parseArguments(String[] args) { + // parse the options + int i = 0; + for (; i < args.length; i++) { + final String argument = args[i]; + if (argument.charAt(0) == '-') { + // is the beginning of a valid option. + final int index = argument.indexOf('='); + final String optionName = getOptionName(argument, index); + String value = getOptionValue(argument, index); + final Syntax syntax = optionSyntax.get(optionName); + // check the syntax of this option + try { + checkSyntax(optionName, syntax, value); + if (syntax == Syntax.CONSUMES_NEXT) { + value = args[++i]; + } + setValue(optionName, value); + } catch (Option.Error error) { + handleErrorDuringParseOrLoad(error, optionName); + } + } else { + // is not an option, therefore the start of arguments + break; + } + } + + final int left = args.length - i; + arguments = new String[left]; + System.arraycopy(args, i, arguments, 0, left); + return this; + } + + /** + * The {@code getArguments()} method gets the leftover command line options + * from the last call to {@code parseArguments}. + * + * @return the leftover command line options + */ + public String[] getArguments() { + if (arguments.length == 0) { + return arguments; + } + return Arrays.copyOf(arguments, arguments.length); + } + + /** + * Determines if this option set allows parsing or loading of unrecognized options. + * @return {@code true} if this option set allows unrecognized options + */ + public boolean allowsUnrecognizedOptions() { + return allowUnrecognizedOptions; + } + + /** + * The {@code loadSystemProperties()} method loads the value of the valid + * options from the systems properties with the specified prefix. + * + * @param prefix the prefix of each system property, used to disambiguate + * these options from other system properties. + * @return this option set + */ + public OptionSet loadSystemProperties(String prefix) { + final Properties systemProperties = System.getProperties(); + final Properties properties = new Properties(); + for (String key : systemProperties.stringPropertyNames()) { + if (key.startsWith(prefix)) { + properties.setProperty(key.substring(prefix.length()), systemProperties.getProperty(key)); + } + } + return loadProperties(properties, true); + } + + /** + * The {@code storeSystemProperties()} method stores these option values + * into the system properties. + * + * @param prefix the prefix to append to all option names when inserting them + * into the systems properties + */ + public void storeSystemProperties(String prefix) { + for (Map.Entry<String, String> entry : optionValues.entrySet()) { + System.setProperty(prefix + entry.getKey(), entry.getValue()); + } + } + + /** + * Loads the specified properties into this set of options. + * + * @param p + * the properties set to load into this set of options + * @param loadall + * true if this method should load all properties in the property set into this option set; false if this + * method should only load properties for options already in this option set + * @return this option set + */ + public OptionSet loadProperties(Properties p, boolean loadall) { + if (loadall) { + // if loadall is specified, load all properties in the set + for (Object object : p.keySet()) { + final String name = (String) object; + final String val = p.getProperty(name); + try { + setValue(name, val); + } catch (Option.Error error) { + handleErrorDuringParseOrLoad(error, name); + } + } + } else { + // if loadall is not specified, only load options that are in this option set. + for (Object o : p.keySet()) { + final String name = (String) o; + if (optionMap.containsKey(name)) { + final String val = p.getProperty(name); + try { + setValue(name, val); + } catch (Option.Error error) { + handleErrorDuringParseOrLoad(error, name); + } + } + } + } + return this; + } + + /** + * The {@code loadFile()} method parses properties from a file and loads them into this set of options. + * + * @param fname + * the filename from while to load the properties + * @param loadall + * true if this method should load all properties in the property set into this option set; false if this + * method should only load properties for options already in this option set + * @return this option set + * @throws java.io.IOException + * if there is a problem opening or reading the file + * @throws Option.Error + * if there is a problem parsing an option + */ + public OptionSet loadFile(String fname, boolean loadall) throws IOException, Option.Error { + final Properties defs = new Properties(); + final FileInputStream stream = new FileInputStream(new File(fname)); + defs.load(stream); + stream.close(); + return loadProperties(defs, loadall); + } + + /** + * Loads a set of options and {@linkplain #getArguments() arguments} from another option set. + * + * @param options the option set from which to load the option values + * @return this option set + */ + public OptionSet loadOptions(OptionSet options) { + for (Map.Entry<String, String> entry : options.optionValues.entrySet()) { + try { + setValue(entry.getKey(), entry.getValue()); + } catch (Option.Error error) { + handleErrorDuringParseOrLoad(error, entry.getKey()); + } + } + arguments = options.arguments; + return this; + } + + protected void checkSyntax(String optname, Syntax syntax, String value) { + if (syntax == Syntax.REQUIRES_BLANK && value != null) { + throw new Option.Error("syntax error: \"-" + optname + "\" required"); + } + if (syntax == Syntax.REQUIRES_EQUALS && value == null) { + throw new Option.Error("syntax error: \"-" + optname + "=value\" required"); + } + if (syntax == Syntax.CONSUMES_NEXT && value != null) { + throw new Option.Error("syntax error: \"-" + optname + " value\" required"); + } + } + + protected String getOptionName(String argument, int equalIndex) { + if (equalIndex < 0) { // naked option + return argument.substring(1, argument.length()); + } + return argument.substring(1, equalIndex); + } + + protected String getOptionValue(String argument, int equalIndex) { + if (equalIndex < 0) { // naked option + return null; + } + return argument.substring(equalIndex + 1); + } + + /** + * Adds the options of an {@link OptionSet} to this set. + * @param optionSet the set of options to add + */ + public void addOptions(OptionSet optionSet) { + for (Option<?> option : optionSet.getOptions()) { + final Syntax syntax = optionSet.optionSyntax.get(option.getName()); + addOption(option, syntax); + } + } + + /** + * The {@code addOption()} method adds an option with the {@link Syntax#REQUIRES_EQUALS} syntax to this option set. + * + * @param option the new option to add to this set + * @return the option passed as the argument, after it has been added to this option set + */ + public <T> Option<T> addOption(Option<T> option) { + return addOption(option, Syntax.REQUIRES_EQUALS); + } + + /** + * The {@code addOption()} method adds an option to this option set. + * + * @param option the new option to add to this set + * @param syntax the syntax of the option, which specifies how to parse the option + * from command line parameters + * @return the option passed as the argument, after it has been added to this option set + */ + public <T> Option<T> addOption(Option<T> option, Syntax syntax) { + final String name = option.getName(); + final Option existingOption = optionMap.put(name, option); + if (existingOption != null) { + throw ProgramError.unexpected("Cannot register more than one option under the same name: " + option.getName()); + } + optionSyntax.put(name, syntax); + return option; + } + + /** + * The {@code setSyntax()} method sets the syntax of a particular option. + * + * @param option the option for which to change the syntax + * @param syntax the new syntax for the instruction + */ + public void setSyntax(Option option, Syntax syntax) { + optionSyntax.put(option.getName(), syntax); + } + + /** + * The {@code setValue()} method sets the value of the specified option in + * this option set. If there is no option by the specified name, the name/value + * pair will simply be remembered. + * + * @param name the name of the option + * @param value the new value of the option as a string + * @throws Option.Error if {@code name} denotes an unrecognized option and this + */ + public void setValue(String name, String value) { + final String v = value == null ? "" : value; + final Option opt = optionMap.get(name); + if (opt != null) { + opt.setString(v); + } else { + if (!allowUnrecognizedOptions) { + throw new Option.Error("unrecognized option -" + name); + } + } + optionValues.put(name, v); + } + + public void setValuesAgain() { + for (String name : optionValues.keySet()) { + final Option opt = optionMap.get(name); + opt.setString(optionValues.get(name)); + } + } + + public String getStringValue(String name) { + return optionValues.get(name); + } + + /** + * The {@code hasOptionSpecified()} method checks whether an option with the specified + * name has been assigned to. An option as been "assigned to" if its value has been set + * by either parsing arguments (the {@code parseArguments() method} or loading properties + * from a file or the system properties. + * + * @param name the name of the option to query + * @return true if an option with the specified name has been set; false otherwise + */ + public boolean hasOptionSpecified(String name) { + return optionValues.containsKey(name); + } + + /** + * Retrieves the options from this option + * set, in the order in which they were added. + * @return an iterable collection of {@code Option} instances, sorted according to insertion order + */ + public Iterable<Option<?>> getOptions() { + return Utils.cast(optionMap.values()); + } + + /** + * The {@code getSortedOptions()} method retrieves the options from this option + * set, sorting all options by their names. + * @return an iterable collection of {@code Option} instances, sorted according to the name of each option + */ + public Iterable<Option<?>> getSortedOptions() { + final List<Option<?>> list = new LinkedList<Option<?>>(); + final TreeSet<String> tree = new TreeSet<String>(); + for (String string : optionMap.keySet()) { + tree.add(string); + } + for (String string : tree) { + list.add(optionMap.get(string)); + } + return list; + } + + /** + * Prints the help message header. + * + * @param stream the output stream to which to write the help text + */ + protected void printHelpHeader(PrintStream stream) { + } + + /** + * The {@code printHelp()} method prints a textual listing of these options and their syntax + * to the specified output stream. + * @param stream the output stream to which to write the help text + * @param width the length of the line to truncate + */ + public void printHelp(PrintStream stream, int width) { + printHelpHeader(stream); + for (Option<?> option : getSortedOptions()) { + if (option.type == OptionTypes.BLANK_BOOLEAN_TYPE && option instanceof FieldOption) { + FieldOption fopt = (FieldOption) option; + try { + if (!fopt.nullValue.equals(fopt.field.getBoolean(null))) { + // Don't show message for "-XX:+<opt>" option if the default is represented by the + // "-XX:-<opt>" option instead (and vice versa). + continue; + } + } catch (Exception e) { + } + } + + + final Option<Object> opt = Utils.cast(option); + stream.print(" " + getUsage(opt)); + final Object defaultValue = opt.getDefaultValue(); + if (defaultValue != null) { + stream.println(" (default: " + opt.getType().unparseValue(defaultValue) + ")"); + } else { + stream.println(); + } + final String help = opt.getHelp(); + if (help.length() > 0) { + stream.print(Strings.formatParagraphs(help, 8, 0, width)); + stream.println(); + } + } + } + + /** + * Prints a textual listing of these options and their values + * to the specified output stream. + * @param stream the output stream to which to write the text + * @param indent the number of spaces to indent + * @param verbose add each option's description when true + */ + public void printValues(PrintStream stream, int indent, boolean verbose) { + final String indentation = Strings.times(' ', indent); + for (Option<?> option : getSortedOptions()) { + if (option.type == OptionTypes.BLANK_BOOLEAN_TYPE && option instanceof FieldOption) { + FieldOption fopt = (FieldOption) option; + try { + if (!fopt.nullValue.equals(fopt.field.getBoolean(null))) { + // Don't show message for "-XX:+<opt>" option if the default is represented by the + // "-XX:-<opt>" option instead (and vice versa). + continue; + } + } catch (Exception e) { + } + } + + + final Option<Object> opt = Utils.cast(option); + stream.print(indentation + opt.getName() + ": " + opt.getValue()); + if (verbose) { + stream.println(opt.isAssigned() ? "" : " (default)"); + stream.println(" " + opt.getHelp()); + stream.println(" " + getUsage(opt)); + } + stream.println(); + } + } + + /** + * This method gets a usage string for a particular option that describes + * the range of valid string values that it accepts. + * @param option the option for which to get usage + * @return a string describing the usage syntax for the specified option + */ + public String getUsage(Option option) { + return optionSyntax.get(option.getName()).getUsage(option); + } + + /** + * This method adds all public static fields with appropriate types to the + * option set with the specified prefix. + * @param javaClass the java class containing the fields + * @param prefix the prefix to add to the options + * @param helpMap map from option names to the help message for the option (may be {@code null}) + */ + public void addFieldOptions(Class<?> javaClass, String prefix, Map<String, String> helpMap) { + for (final Field field : javaClass.getDeclaredFields()) { + int modifiers = field.getModifiers(); + if (Modifier.isStatic(modifiers) && !Modifier.isFinal(modifiers)) { + field.setAccessible(true); + final OptionSettings settings = field.getAnnotation(OptionSettings.class); + String help; + String name; + if (settings != null) { + help = settings.help(); + name = settings.name().isEmpty() ? field.getName().replace('_', '-') : settings.name(); + } else { + name = field.getName().replace('_', '-'); + help = helpMap != null ? helpMap.get(name) : ""; + if (help == null) { + help = ""; + } + } + addFieldOption(prefix, name, null, field, help); + } + } + } + + /** + * Adds a new option whose value is stored in the specified reflection field. + * @param prefix the name of the option + * @param name the name of the option + * @param object the object containing the field (if the field is not static) + * @param field the field to store the value + * @param help the help text for the option @return a new option that will modify the field when parsed + * @return the option created + */ + @SuppressWarnings("unchecked") + public Option<?> addFieldOption(String prefix, String name, Object object, Field field, String help) { + final Class<?> fieldType = field.getType(); + final Object defaultValue; + final String optionName = prefix + ":" + name; + try { + defaultValue = field.get(null); + } catch (IllegalAccessException e) { + return null; + } + if (fieldType == boolean.class) { + if (prefix.length() > 0) { + // setup a "-prefix+name" option + String plusName = prefix + ":+" + name; + FieldOption<Boolean> plusOption = new FieldOption<Boolean>(plusName, object, field, (Boolean) defaultValue, OptionTypes.BLANK_BOOLEAN_TYPE, help); + plusOption.nullValue = true; + addOption(plusOption, Syntax.REQUIRES_BLANK); + + // setup a "-prefix-name" option + String minusName = prefix + ":-" + name; + FieldOption<Boolean> minusOption = new FieldOption<Boolean>(minusName, object, field, (Boolean) defaultValue, OptionTypes.BLANK_BOOLEAN_TYPE, help); + minusOption.nullValue = false; + return addOption(minusOption, Syntax.REQUIRES_BLANK); + } + return addOption(new FieldOption<Boolean>(optionName, object, field, (Boolean) defaultValue, OptionTypes.BOOLEAN_TYPE, help)); + + } else if (fieldType == int.class) { + return addOption(new FieldOption<Integer>(optionName, object, field, (Integer) defaultValue, OptionTypes.INT_TYPE, help)); + } else if (fieldType == float.class) { + return addOption(new FieldOption<Float>(optionName, object, field, (Float) defaultValue, OptionTypes.FLOAT_TYPE, help)); + } else if (fieldType == long.class) { + return addOption(new FieldOption<Long>(optionName, object, field, (Long) defaultValue, OptionTypes.LONG_TYPE, help)); + } else if (fieldType == double.class) { + return addOption(new FieldOption<Double>(optionName, object, field, (Double) defaultValue, OptionTypes.DOUBLE_TYPE, help)); + } else if (fieldType == String.class) { + return addOption(new FieldOption<String>(optionName, object, field, (String) defaultValue, OptionTypes.STRING_TYPE, help)); + } else if (fieldType == File.class) { + return addOption(new FieldOption<File>(optionName, object, field, (File) defaultValue, OptionTypes.FILE_TYPE, help)); + } else if (fieldType.isEnum()) { + final Class<? extends Enum> enumClass = Utils.cast(fieldType); + return addOption(makeEnumFieldOption(optionName, object, field, defaultValue, enumClass, help)); + } + return null; + } + + private <T extends Enum<T>> FieldOption<T> makeEnumFieldOption(String name, Object object, Field field, Object defaultValue, Class<T> enumClass, String help) { + final OptionTypes.EnumType<T> optionType = new OptionTypes.EnumType<T>(enumClass); + final T defaultV = Utils.<T>cast(defaultValue); + return new FieldOption<T>(name, object, field, defaultV, optionType, help); + } + + public Option<String> newStringOption(String name, String defaultValue, String help) { + return addOption(new Option<String>(name, defaultValue, OptionTypes.STRING_TYPE, help)); + } + + public Option<Integer> newIntegerOption(String name, Integer defaultValue, String help) { + return addOption(new Option<Integer>(name, defaultValue, OptionTypes.INT_TYPE, help)); + } + + public Option<Long> newLongOption(String name, Long defaultValue, String help) { + return addOption(new Option<Long>(name, defaultValue, OptionTypes.LONG_TYPE, help)); + } + + public Option<Float> newFloatOption(String name, Float defaultValue, String help) { + return addOption(new Option<Float>(name, defaultValue, OptionTypes.FLOAT_TYPE, help)); + } + + public Option<Double> newDoubleOption(String name, Double defaultValue, String help) { + return addOption(new Option<Double>(name, defaultValue, OptionTypes.DOUBLE_TYPE, help)); + } + + public Option<List<String>> newStringListOption(String name, String defaultValue, String help) { + return addOption(new Option<List<String>>(name, defaultValue == null ? null : OptionTypes.COMMA_SEPARATED_STRING_LIST_TYPE.parseValue(defaultValue), OptionTypes.COMMA_SEPARATED_STRING_LIST_TYPE, help)); + } + + public Option<List<String>> newStringListOption(String name, String[] defaultValue, String help) { + List<String> list = null; + if (defaultValue != null) { + list = new ArrayList<String>(defaultValue.length); + list.addAll(Arrays.asList(defaultValue)); + } + return addOption(new Option<List<String>>(name, list, OptionTypes.COMMA_SEPARATED_STRING_LIST_TYPE, help)); + } + + public Option<List<String>> newStringListOption(String name, String defaultValue, char separator, String help) { + final OptionTypes.StringListType type = new OptionTypes.StringListType(separator); + return addOption(new Option<List<String>>(name, defaultValue == null ? null : type.parseValue(defaultValue), type, help)); + } + + public <T> Option<List<T>> newListOption(String name, String defaultValue, Option.Type<T> elementOptionType, char separator, String help) { + final OptionTypes.ListType<T> type = new OptionTypes.ListType<T>(separator, elementOptionType); + return addOption(new Option<List<T>>(name, defaultValue == null ? null : type.parseValue(defaultValue), type, help)); + } + + public Option<File> newFileOption(String name, String defaultValue, String help) { + return newFileOption(name, OptionTypes.FILE_TYPE.parseValue(defaultValue), help); + } + + public Option<File> newFileOption(String name, File defaultValue, String help) { + return addOption(new Option<File>(name, defaultValue, OptionTypes.FILE_TYPE, help)); + } + + public Option<URL> newURLOption(String name, URL defaultValue, String help) { + return addOption(new Option<URL>(name, defaultValue, OptionTypes.URL_TYPE, help)); + } + + public <T> Option<T> newInstanceOption(String name, Class<T> klass, T defaultValue, String help) { + return addOption(new Option<T>(name, defaultValue, OptionTypes.createInstanceOptionType(klass), help)); + } + + public <T> Option<List<T>> newListInstanceOption(String name, String defaultValue, Class<T> klass, char separator, String help) { + final OptionTypes.ListType<T> type = OptionTypes.createInstanceListOptionType(klass, separator); + return addOption(new Option<List<T>>(name, (defaultValue == null) ? null : type.parseValue(defaultValue), type, help)); + } + + public Option<Boolean> newBooleanOption(String name, Boolean defaultValue, String help) { + if (defaultValue != null && !defaultValue) { + return addOption(new Option<Boolean>(name, defaultValue, OptionTypes.BOOLEAN_TYPE, help), Syntax.EQUALS_OR_BLANK); + } + return addOption(new Option<Boolean>(name, defaultValue, OptionTypes.BOOLEAN_TYPE, help)); + } + + public <T> Option<T> newOption(String name, String defaultValue, Option.Type<T> type, String help) { + return newOption(name, type.parseValue(defaultValue), type, Syntax.REQUIRES_EQUALS, help); + } + + public <T> Option<T> newOption(String name, T defaultValue, Option.Type<T> type, Syntax syntax, String help) { + return addOption(new Option<T>(name, defaultValue, type, help), syntax); + } + + public <E extends Enum<E>> Option<E> newEnumOption(String name, E defaultValue, Class<E> enumClass, String help) { + return addOption(new Option<E>(name, defaultValue, new OptionTypes.EnumType<E>(enumClass), help)); + } + + public <E extends Enum<E>> Option<List<E>> newEnumListOption(String name, Iterable<E> defaultValue, Class<E> enumClass, String help) { + final List<E> list; + if (defaultValue == null) { + list = null; + } else if (defaultValue instanceof List) { + list = Utils.cast(defaultValue); + } else if (defaultValue instanceof Collection) { + final Collection<E> collection = Utils.cast(defaultValue); + list = new ArrayList<E>(collection); + } else { + list = new ArrayList<E>(); + for (E value : defaultValue) { + list.add(value); + } + } + final Option<List<E>> option = new Option<List<E>>(name, list, new OptionTypes.EnumListType<E>(enumClass, ','), help); + return addOption(option); + } + + public Option<File> newConfigOption(String name, File defaultFile, String help) { + return addOption(new Option<File>(name, defaultFile, new OptionTypes.ConfigFile(this), help)); + } +}