org.etlunit.util.cli.CommonsCLILauncher Maven / Gradle / Ivy
package org.etlunit.util.cli;
import org.apache.commons.cli.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
public class CommonsCLILauncher
{
enum data_type
{
bool,
Bool,
integer,
Integer,
string,
String,
Object
}
enum cardinal_type
{
instance,
array,
flag
}
private static final class Data
{
data_type type = null;
cardinal_type cardinality = null;
Method setter;
CLIOption clioption;
}
public static CLIEntry main(String[] argv)
{
// the protocol is system property, then call stack
String val = System.getProperty("org.etlunit.util.cli.Main");
if (val != null)
{
try
{
launch(val, argv);
}
catch (Exception e)
{
throw new IllegalArgumentException("Error involing class from system property", e);
}
}
// determine the appropriate caller
StackTraceElement[] st = Thread.currentThread().getStackTrace();
// ignoring the last element, which will be the current class, move up the chain
// and grab the first CLIEntry you find
for (int i = 1; i < st.length; i++)
{
String name = st[i].getClassName();
try
{
return launch(name, argv);
}
catch (CommandLineProcessingException e)
{
throw e;
}
catch (Exception e)
{
// pass over this class. WTHeck!?!?
}
}
// fail loudly
throw new IllegalArgumentException("No acceptable CLIEntry found on the stack.");
}
private static CLIEntry launch(String name, String [] argv)
throws ClassNotFoundException, IllegalAccessException, InstantiationException
{
Class cl = Class.forName(name);
if (CLIEntry.class.isAssignableFrom(cl))
{
// try this one
CLIEntry clie = (CLIEntry) cl.newInstance();
mainWithInstance(clie, argv);
// bail - our work is done
return clie;
}
throw new InstantiationException();
}
public static CLIEntry mainWithInstance(CLIEntry obj, String[] argv) throws CommandLineProcessingException
{
Options options = new Options();
Map typeMap = new HashMap();
// look through the annotations for methods of this class, and create options for each
Method[] methList = obj.getClass().getMethods();
for (int i = 0; i < methList.length; i++)
{
CLIOption annot = methList[i].getAnnotation(CLIOption.class);
if (annot != null)
{
// validate that this is a method which accepts a single parameter
Class>[] parameterTypes = methList[i].getParameterTypes();
Data data = new Data();
data.setter = methList[i];
data.clioption = annot;
if (typeMap.containsKey(annot.name()))
{
throw new CommandLineProcessingException("Option [" + annot.name() + "] specified more than once");
}
typeMap.put(annot.name(), data);
if (parameterTypes == null || parameterTypes.length != 1)
{
throw new CommandLineProcessingException("Setter for option ["
+ annot.name()
+ "] does not accept exactly one parameter: "
+ methList[i]);
}
// if the cardinality is anything other than 1, the parameter must be an array type
Class> parameterType = parameterTypes[0];
Class> arrayComponentType = parameterType.isArray() ? parameterType.getComponentType() : parameterType;
if (parameterType.isArray())
{
data.cardinality = cardinal_type.array;
}
else
{
data.cardinality = cardinal_type.instance;
}
// validate that the type of the parameter is within bounds
if (arrayComponentType.equals(Object.class))
{
data.type = data_type.Object;
}
else if (arrayComponentType.equals(String.class))
{
data.type = data_type.String;
}
else if (arrayComponentType.equals(boolean.class))
{
data.type = data_type.bool;
if (data.cardinality != cardinal_type.array)
{
data.cardinality = cardinal_type.flag;
}
}
else if (arrayComponentType.equals(Boolean.class))
{
data.type = data_type.Bool;
if (data.cardinality != cardinal_type.array)
{
data.cardinality = cardinal_type.flag;
}
}
else if (arrayComponentType.equals(int.class))
{
data.type = data_type.integer;
}
else if (arrayComponentType.equals(Integer.class))
{
data.type = data_type.Integer;
}
if (data.type == null)
{
throw new CommandLineProcessingException("Setter for option ["
+ annot.name()
+ "] does not accept a valid parameter type: "
+ methList[i]);
}
if (annot.valueCardinality() == 1)
{
if (data.cardinality == cardinal_type.array)
{
throw new CommandLineProcessingException("Setter for option ["
+ annot.name()
+ "] must not use an array type: "
+ methList[i]);
}
}
else
{
if (data.cardinality != cardinal_type.array)
{
throw new CommandLineProcessingException("Setter for option ["
+ annot.name()
+ "] requires an array type: "
+ methList[i]);
}
}
//here is an option - check it out
Option option = new Option(
annot.name(),
annot.longName().equals("") ? null : annot.longName(),
annot.valueType() != CLIOption.value_type.not_allowed,
annot.description()
);
option.setRequired(annot.required());
// in the case of arguments with values, get the value cardinalities worked out
if (annot.valueType() != CLIOption.value_type.not_allowed)
{
option.setArgs(1);
if (annot.valueType() == CLIOption.value_type.required)
{
option.setOptionalArg(false);
}
else if (annot.valueType() == CLIOption.value_type.optional)
{
option.setOptionalArg(true);
}
}
else
{
option.setArgs(0);
option.setOptionalArg(false);
}
//option.setType();
options.addOption(option);
}
}
//options are created - pass the result to the command line
CommandLineParser clp = null;
switch (obj.getParserType())
{
case gnu:
clp = new GnuParser();
break;
case posix:
clp = new PosixParser();
break;
case basic:
clp = new BasicParser();
break;
}
try
{
CommandLine cl = clp.parse(options, argv);
// run through all options and pass the values on with injection
for (Map.Entry toption : typeMap.entrySet())
{
Data data = toption.getValue();
boolean opted = cl.hasOption(data.clioption.name());
Object value = data.clioption.defaultValue();
if (!opted)
{
if (data.clioption.defaultValue().equals("") && data.clioption.required())
{
throw new CommandLineProcessingException("Value not specified [" + data.clioption.name() + "] and no suitable default exists");
}
}
value = resolveOptionValue(data, cl, data.clioption.defaultValue().equals("") ? null : data.clioption.defaultValue());
if (value != null)
{
try
{
data.setter.invoke(obj, value);
}
catch (IllegalAccessException e)
{
throw new CommandLineProcessingException("Could not access setter for option [" + data.clioption.name() + "]", e);
}
catch (InvocationTargetException e)
{
throw new CommandLineProcessingException("Error setting option value [" + data.clioption.name() + "]", e);
}
}
}
obj.setRawCommandLine(argv);
obj.main();
return obj;
}
catch (ParseException e)
{
System.out.println(e.getMessage());
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("usage", options);
throw new CommandLineProcessingException(e);
}
}
private static Object resolveOptionValue(Data data, CommandLine cl, String def)
{
String optionName = data.clioption.name();
Method setter = data.setter;
String textValue = cl.hasOption(data.clioption.name()) ? cl.getOptionValue(data.clioption.name()) : def;
if (textValue == null)
{
return null;
}
Object value = null;
// we already validated earlier that there is exactly one parameter
if (data.cardinality != cardinal_type.flag)
{
if (data.cardinality == cardinal_type.array)
{
String[] optionValue = getArrayValue(textValue, data.clioption.valueSeparator());
if (data.clioption.valueCardinality() != CLIOption.UNLIMITED_VALUES)
{
if (optionValue.length != data.clioption.valueCardinality())
{
throw new CommandLineProcessingException("Wrong number of values supplied for option [" + data.clioption.name() + "]: " + optionValue);
}
}
Object[] valueArr = optionValue;
boolean[] bValueArr = null;
int[] iValueArr = null;
if (data.type == data_type.bool)
{
bValueArr = new boolean[optionValue.length];
}
else if (data.type == data_type.Bool)
{
valueArr = new Boolean[optionValue.length];
}
else if (data.type == data_type.integer)
{
iValueArr = new int[optionValue.length];
}
else if (data.type == data_type.Integer)
{
valueArr = new Integer[optionValue.length];
}
else if (data.type == data_type.String)
{
valueArr = new String[optionValue.length];
}
for (int optIndex = 0; optIndex < optionValue.length; optIndex++)
{
if (data.type == data_type.Bool)
{
valueArr[optIndex] = Boolean.valueOf(optionValue[optIndex]);
}
else if (data.type == data_type.bool)
{
bValueArr[optIndex] = Boolean.valueOf(optionValue[optIndex]);
}
else if (data.type == data_type.integer)
{
iValueArr[optIndex] = Integer.parseInt(optionValue[optIndex]);
}
else if (data.type == data_type.Integer)
{
valueArr[optIndex] = Integer.parseInt(optionValue[optIndex]);
}
else if (data.type == data_type.String || data.type == data_type.Object)
{
valueArr[optIndex] = optionValue[optIndex];
}
}
if (bValueArr != null)
{
value = bValueArr;
}
else if (iValueArr != null)
{
value = iValueArr;
}
else
{
value = valueArr;
}
}
else
{
String optionValue = textValue;
if (!cl.hasOption(data.clioption.name()))
{
optionValue = def;
}
value = optionValue;
if (data.type == data_type.Bool || data.type == data_type.bool)
{
value = Boolean.valueOf(optionValue);
}
else if (data.type == data_type.integer || data.type == data_type.Integer)
{
value = Integer.parseInt(optionValue);
}
}
}
else
{
value = true;
}
return value;
}
private static String[] getArrayValue(String textValue, String c)
{
return textValue.split(Pattern.quote(c));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy