All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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