/*
* Copyright 2004-2010 Information & Software Engineering Group (188/1)
* Institute of Software Technology and Interactive Systems
* Vienna University of Technology, Austria
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.ifs.tuwien.ac.at/dm/somtoolbox/license.html
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package at.tuwien.ifs.somtoolbox.apps.config;
import java.io.PrintStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.logging.Logger;
import com.martiansoftware.jsap.FlaggedOption;
import com.martiansoftware.jsap.JSAP;
import com.martiansoftware.jsap.JSAPException;
import com.martiansoftware.jsap.JSAPResult;
import com.martiansoftware.jsap.Parameter;
import com.martiansoftware.jsap.Switch;
import com.martiansoftware.jsap.stringparsers.EnumeratedStringParser;
import com.martiansoftware.jsap.stringparsers.FileStringParser;
import at.tuwien.ifs.commons.util.io.ExtensionFileFilterSwing;
import at.tuwien.ifs.somtoolbox.apps.SOMToolboxMain;
/**
* @author Rudolf Mayer
* @version $Id: AbstractOptionFactory.java 3753 2010-08-17 09:12:29Z mayer $
*/
public class AbstractOptionFactory {
private static class FieldComparator implements Comparator<Field> {
@Override
public int compare(Field o1, Field o2) {
return o1.getName().compareTo(o2.getName());
}
}
private static class MethodComparator implements Comparator<Method> {
@Override
public int compare(Method o1, Method o2) {
return o1.getName().compareTo(o2.getName());
}
}
public static Logger logger;
public static EnumeratedStringParser makeEnumeratedStringParser(String... validOptionValues) {
return EnumeratedStringParser.getParser(at.tuwien.ifs.somtoolbox.util.StringUtils.toString(validOptionValues,
"", "", Character.toString(EnumeratedStringParser.CONSTRUCTOR_VALUE_SEPARATOR)), true, false);
}
public static JSAPResult parseResults(Class<?> callingClass, String[] args, Parameter... options) {
logger = Logger.getLogger(callingClass.getPackage().getName());
return parseResults(args, registerOptions(options), callingClass.getName());
}
public static JSAPResult parseResults(Class<?> callingClass, String[] args, boolean printParameterValues,
Parameter... options) {
JSAPResult parseResults = parseResults(args, registerOptions(options), callingClass.getName());
if (printParameterValues) {
logger.info(AbstractOptionFactory.toString(parseResults, options));
}
return parseResults;
}
public static JSAPResult parseResults(String[] args, JSAP jsap) {
return parseResults(args, jsap, null);
}
public static JSAPResult parseResults(String[] args, Parameter... options) {
return parseResults(args, registerOptions(options), null);
}
public static JSAPResult parseResults(String[] args, boolean printParameterValues, Parameter... options) {
JSAPResult parseResults = parseResults(args, registerOptions(options), null);
if (printParameterValues) {
logger.info(AbstractOptionFactory.toString(parseResults, options));
}
return parseResults;
}
public static JSAPResult parseResults(String[] args, JSAP jsap, String className) {
try {
jsap.registerParameter(new Switch("gui", 'G', "gui", "Show the graphical interface for additional options."));
} catch (JSAPException e3) {
}
try {
jsap.registerParameter(new Switch("help", JSAP.NO_SHORTFLAG, "help", "Print this help and exit."));
} catch (JSAPException e2) {
}
try {
jsap.registerParameter(new Switch("version", JSAP.NO_SHORTFLAG, "version", "Print the version and exit."));
} catch (JSAPException e1) {
}
JSAPResult config = jsap.parse(args);
if (className == null) {
className = computeClassName();
}
if (config.getBoolean("help")) {
printHelp(jsap, className, System.out);
System.exit(0);
}
if (config.getBoolean("version")) {
printVersion(className);
System.exit(0);
}
if (!config.success()) {
printUsage(jsap, className, config, null);
}
try {
logger = Logger.getLogger(Class.forName(className).getPackage().getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return config;
}
public static String toString(JSAPResult result, Parameter[] options) {
StringBuffer sb = new StringBuffer();
for (Parameter option : options) {
String optionsId = option.getID();
String optionsValue = String.valueOf(result.getObject(optionsId));
sb.append(optionsId + ": " + optionsValue + "\n");
}
return sb.toString();
}
private static String computeClassName() {
StackTraceElement[] stackTrace = new Throwable().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if (!stackTraceElement.getClassName().equals(AbstractOptionFactory.class.getName())) {
return stackTraceElement.getClassName();
}
}
return "<unknown class>";
}
public static void printUsage(JSAP jsap, String className, JSAPResult config, String errorMessage) {
// print out specific error messages describing the problems
// with the command line, THEN print usage, THEN print full
// help. This is called "beating the user with a clue stick."
System.err.println();
for (Iterator<?> errs = config.getErrorMessageIterator(); errs.hasNext();) {
System.err.println("Error: " + errs.next());
}
if (errorMessage != null && !errorMessage.trim().equals("")) {
System.err.println("Error: " + errorMessage);
}
System.err.println();
printHelp(jsap, className, System.err);
System.exit(-1);
}
public static void printHelp(JSAP jsap, String className, PrintStream outStream) {
outStream.println("Usage: java " + className + " " + jsap.getUsage());
outStream.println();
outStream.println("Options:");
// Replacements for better man-pages
outStream.println(jsap.getHelp(120).replaceAll("[\\[\\]()]", "").replaceAll("\\|", ", "));
}
private static void printVersion(String className) {
String simpleClassName = className.substring(className.lastIndexOf('.') + 1);
SOMToolboxMain.printVersion(simpleClassName);
}
/**
* Register a given set of options to the given JSAP object.
*
* @param jsap The JSAP to register options to.
* @param options The options to register.
*/
public static void registerOptions(JSAP jsap, Parameter[] options) {
for (Parameter element : options) {
try {
jsap.registerParameter(element);
} catch (JSAPException e) {
logger.severe(e.getMessage());
}
}
}
public static JSAP registerOptions(Parameter[] options) {
JSAP jsap = new JSAP();
for (Parameter element : options) {
try {
jsap.registerParameter(element);
} catch (JSAPException e) {
logger.severe(e.getMessage());
}
}
return jsap;
}
public static String getFilePath(JSAPResult config, String id) {
if (config.contains(id)) {
return config.getFile(id).getAbsolutePath();
} else {
return null;
}
}
protected static FileStringParser getOuputFileParser() {
return FileStringParser.getParser().setMustBeFile(true);
}
protected static FileStringParser getInputFileParser() {
// FIXME: setMustExist is a hard condition if we have a file where we also allow skipping the .gz extension
// this is basically the case for most input files...
return FileStringParser.getParser().setMustBeFile(true);// .setMustExist(true);
}
protected static FileStringParser getInputFileParser(String... extension) {
return getInputFileParser().setFileFilter(new ExtensionFileFilterSwing(extension));
}
protected static FileStringParser getOutputDirectoryParser() {
return FileStringParser.getParser().setMustBeDirectory(true);
}
protected static FileStringParser getInputDirectoryParser() {
return FileStringParser.getParser().setMustBeDirectory(true).setMustExist(true);
}
protected static void testDuplicateOptions(Object o) {
Class<?> c = o.getClass();
ArrayList<Field> paramFields = new ArrayList<Field>();
Field[] fields = c.getFields();
for (Field field : fields) {
if (Parameter.class.isAssignableFrom(field.getClass())) {
paramFields.add(field);
} else {
System.out.println("Skipping field: " + field.getName() + " (" + field + ")");
}
}
Collections.sort(paramFields, new FieldComparator());
ArrayList<Method> paramMethods = new ArrayList<Method>();
Method[] methods = c.getMethods();
for (Method method : methods) {
if (Parameter.class.isAssignableFrom(method.getReturnType())) {
paramMethods.add(method);
} else {
System.out.println("Skipping method: " + method.getName() + " (" + method + ")");
}
}
Collections.sort(paramMethods, new MethodComparator());
System.out.println("\n");
HashMap<Parameter, String> shortFlags = new HashMap<Parameter, String>();
HashMap<Parameter, String> longFlags = new HashMap<Parameter, String>();
HashMap<Parameter, String> ids = new HashMap<Parameter, String>();
System.out.println("Fields containing Parameters: ");
if (paramFields.size() == 0) {
System.out.println("\tnone");
}
for (Field field : paramFields) {
System.out.println("\t" + field.getName() + " (" + field + ")");
try {
Parameter parameter = (Parameter) field.get(o);
checkParamter(shortFlags, longFlags, ids, parameter);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
System.out.println("Methods containing Parameters: ");
for (Method method : paramMethods) {
System.out.print("\t" + method.getName() + "\t(");
Class<?>[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
Class<?> class1 = parameterTypes[i];
System.out.print(class1.getSimpleName());
if (i + 1 < parameterTypes.length) {
System.out.print(", ");
}
}
System.out.print(")");
try {
Class<?>[] paramTypes = method.getParameterTypes();
Object[] args = new Object[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++) {
Class<?> class1 = paramTypes[i];
if (class1.getName().equalsIgnoreCase("boolean")) {
args[i] = true;
} else if (class1.getName().equalsIgnoreCase("int")) {
args[i] = 1;
} else {
args[i] = null;
}
}
System.out.println(" => args: " + Arrays.toString(args));
Parameter parameter = (Parameter) method.invoke(o, args);
checkParamter(shortFlags, longFlags, ids, parameter);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
private static void checkParamter(HashMap<Parameter, String> shortFlags, HashMap<Parameter, String> longFlags,
HashMap<Parameter, String> ids, Parameter parameter) {
checkDuplicates(ids, parameter, "id", parameter.getID());
if (parameter instanceof FlaggedOption || parameter instanceof Switch) {
String longFlag;
char shortFlag;
if (parameter instanceof FlaggedOption) {
longFlag = ((FlaggedOption) parameter).getLongFlag();
shortFlag = ((FlaggedOption) parameter).getShortFlag();
} else {
longFlag = ((Switch) parameter).getLongFlag();
shortFlag = ((Switch) parameter).getShortFlag();
}
if (longFlag != JSAP.NO_LONGFLAG) {
checkDuplicates(longFlags, parameter, "longFlag", longFlag);
}
if (shortFlag != JSAP.NO_SHORTFLAG) {
checkDuplicates(shortFlags, parameter, "shortFlag", shortFlag);
}
}
}
private static void checkDuplicates(HashMap<Parameter, String> map, Parameter parameter, String propertyName,
Character propertyValue) {
checkDuplicates(map, parameter, propertyName, String.valueOf(propertyValue));
}
private static void checkDuplicates(HashMap<Parameter, String> map, Parameter parameter, String propertyName,
String propertyValue) {
if (map.containsValue(propertyValue)) {
System.out.println("Duplicate " + propertyName + " '" + propertyValue + "'");
}
map.put(parameter, propertyValue);
}
protected static ArrayList<Parameter> findDuplicates(HashMap<Parameter, ?> map, Object value) {
ArrayList<Parameter> res = new ArrayList<Parameter>();
for (Map.Entry<Parameter, ?> entry : map.entrySet()) {
if (entry.getValue().equals(value)) {
res.add(entry.getKey());
}
}
return res;
}
}