Bug fixes and support for mutually exclusive options. Still a bit rough, but will
be easier to clean up after a walker refactoring. git-svn-id: file:///humgen/gsa-scr1/gsa-engineering/svn_contents/trunk@198 348d0f76-0448-11de-a6fe-93d51630548a
This commit is contained in:
parent
f47a214f96
commit
08ece8df79
|
|
@ -23,4 +23,5 @@ public @interface Argument {
|
||||||
String shortName() default "";
|
String shortName() default "";
|
||||||
String doc() default "";
|
String doc() default "";
|
||||||
boolean required() default true;
|
boolean required() default true;
|
||||||
|
String exclusive() default "";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,9 @@ import java.lang.reflect.Type;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.broadinstitute.sting.utils.Pair;
|
import org.broadinstitute.sting.utils.Pair;
|
||||||
|
|
||||||
|
|
@ -34,9 +37,6 @@ public class ArgumentParser {
|
||||||
// what program are we parsing for
|
// what program are we parsing for
|
||||||
private String programName;
|
private String programName;
|
||||||
|
|
||||||
// our m_options
|
|
||||||
private ArrayList<String> m_option_names = new ArrayList<String>();
|
|
||||||
|
|
||||||
// where we eventually want the values to land
|
// where we eventually want the values to land
|
||||||
private HashMap<String, Pair<Object,Field>> m_storageLocations = new HashMap<String, Pair<Object,Field>>();
|
private HashMap<String, Pair<Object,Field>> m_storageLocations = new HashMap<String, Pair<Object,Field>>();
|
||||||
|
|
||||||
|
|
@ -91,7 +91,7 @@ public class ArgumentParser {
|
||||||
.create(letterform);
|
.create(letterform);
|
||||||
|
|
||||||
// add it to the option
|
// add it to the option
|
||||||
AddToOptionStorage(name, letterform, fieldname, opt);
|
AddToOptionStorage(opt, fieldname);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -99,37 +99,50 @@ public class ArgumentParser {
|
||||||
/**
|
/**
|
||||||
* Used locally to add to the options storage we have, for latter processing
|
* Used locally to add to the options storage we have, for latter processing
|
||||||
*
|
*
|
||||||
* @param name the name of the option
|
* @param opt the option
|
||||||
* @param letterform it's short form
|
|
||||||
* @param fieldname what field it should be stuck into on the calling class
|
* @param fieldname what field it should be stuck into on the calling class
|
||||||
* @param opt the option
|
|
||||||
*/
|
*/
|
||||||
private void AddToOptionStorage(String name, String letterform, String fieldname, Option opt) {
|
private void AddToOptionStorage(Option opt, String fieldname) {
|
||||||
AddToOptionStorage(name, letterform, getField(prog, fieldname), opt);
|
AddToOptionStorage( opt, getField(prog, fieldname) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used locally to add to the options storage we have, for latter processing
|
* Used locally to add to the options storage we have, for latter processing
|
||||||
*
|
*
|
||||||
* @param name the name of the option
|
|
||||||
* @param letterform it's short form
|
|
||||||
* @param field what field it should be stuck into on the calling class
|
|
||||||
* @param opt the option
|
* @param opt the option
|
||||||
|
* @param field what field it should be stuck into on the calling class
|
||||||
*/
|
*/
|
||||||
private void AddToOptionStorage(String name, String letterform, Pair<Object,Field> field, Option opt) {
|
private void AddToOptionStorage(Option opt, Pair<Object,Field> field ) {
|
||||||
|
// first check to see if we've already added an option with the same name
|
||||||
|
if (m_options.hasOption( opt.getOpt() ))
|
||||||
|
throw new IllegalArgumentException(opt.getOpt() + " was already added as an option");
|
||||||
|
|
||||||
// add to the option list
|
// add to the option list
|
||||||
m_options.addOption(opt);
|
m_options.addOption(opt);
|
||||||
|
|
||||||
// first check to see if we've already added an option with the same name
|
// add the object with it's name to the storage location
|
||||||
if (m_option_names.contains(letterform)) {
|
m_storageLocations.put( opt.getLongOpt(), field );
|
||||||
throw new IllegalArgumentException(letterform + " was already added as an option");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// add the object with it's name to the storage location
|
/**
|
||||||
m_storageLocations.put( name, field );
|
* Used locally to add a group of mutually exclusive options to options storage.
|
||||||
|
* @param options A list of pairs of param, field to add.
|
||||||
|
*/
|
||||||
|
private void AddToOptionStorage( List<Pair<Option,Pair<Object,Field>>> options ) {
|
||||||
|
OptionGroup optionGroup = new OptionGroup();
|
||||||
|
boolean isRequired = true;
|
||||||
|
|
||||||
// add to the list of m_options
|
for( Pair<Option,Pair<Object,Field>> option: options ) {
|
||||||
m_option_names.add(letterform);
|
if (m_options.hasOption(option.first.getOpt()) )
|
||||||
|
throw new IllegalArgumentException(option.first.getOpt() + " was already added as an option");
|
||||||
|
|
||||||
|
optionGroup.addOption(option.first);
|
||||||
|
m_storageLocations.put( option.first.getLongOpt(), option.second );
|
||||||
|
isRequired &= option.first.isRequired();
|
||||||
|
}
|
||||||
|
|
||||||
|
optionGroup.setRequired(isRequired);
|
||||||
|
m_options.addOptionGroup(optionGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pair<Object,Field> getField( Object obj, String fieldName ) {
|
private Pair<Object,Field> getField( Object obj, String fieldName ) {
|
||||||
|
|
@ -161,7 +174,7 @@ public class ArgumentParser {
|
||||||
.create(letterform);
|
.create(letterform);
|
||||||
|
|
||||||
// add it to the option
|
// add it to the option
|
||||||
AddToOptionStorage(name, letterform, fieldname, opt);
|
AddToOptionStorage( opt, fieldname );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -182,7 +195,7 @@ public class ArgumentParser {
|
||||||
.withDescription(description)
|
.withDescription(description)
|
||||||
.create(letterform);
|
.create(letterform);
|
||||||
// add it to the option
|
// add it to the option
|
||||||
AddToOptionStorage(name, letterform, fieldname, opt);
|
AddToOptionStorage( opt, fieldname );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -207,7 +220,7 @@ public class ArgumentParser {
|
||||||
.withDescription("(Required Option) " + description)
|
.withDescription("(Required Option) " + description)
|
||||||
.create(letterform);
|
.create(letterform);
|
||||||
// add it to the option
|
// add it to the option
|
||||||
AddToOptionStorage(name, letterform, fieldname, opt);
|
AddToOptionStorage(opt, fieldname);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -239,7 +252,7 @@ public class ArgumentParser {
|
||||||
|
|
||||||
|
|
||||||
// add it to the option
|
// add it to the option
|
||||||
AddToOptionStorage(name, letterform, fieldname, opt);
|
AddToOptionStorage( opt, fieldname );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -272,7 +285,7 @@ public class ArgumentParser {
|
||||||
.create(letterform);
|
.create(letterform);
|
||||||
|
|
||||||
// add it to the option
|
// add it to the option
|
||||||
AddToOptionStorage(name, letterform, fieldname, opt);
|
AddToOptionStorage( opt, fieldname );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -333,17 +346,88 @@ public class ArgumentParser {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract arguments stored in annotations from fields of a given class.
|
* Extract arguments stored in annotations from fields of a given class.
|
||||||
* @param source
|
* @param source Source of arguments, probably provided through Argument annotation.
|
||||||
*/
|
*/
|
||||||
public void addArgumentSource( Object source ) {
|
public void addArgumentSource( Object source ) {
|
||||||
Field[] fields = source.getClass().getFields();
|
Field[] fields = source.getClass().getFields();
|
||||||
|
|
||||||
|
for( Set<Field> optionGroup: groupExclusiveOptions(fields) ) {
|
||||||
|
List<Pair<Option,Pair<Object,Field>>> options = new ArrayList<Pair<Option,Pair<Object,Field>>>();
|
||||||
|
for( Field field: optionGroup ) {
|
||||||
|
Argument argument = field.getAnnotation(Argument.class);
|
||||||
|
Option option = createOptionFromField( source, field, argument );
|
||||||
|
options.add( new Pair<Option,Pair<Object,Field>>( option, new Pair<Object,Field>( source,field) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( options.size() == 1 )
|
||||||
|
AddToOptionStorage( options.get(0).first, new Pair<Object,Field>( source, options.get(0).second.second ) );
|
||||||
|
else {
|
||||||
|
AddToOptionStorage( options );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Group mutually exclusive options together into sets. Non-exclusive options
|
||||||
|
* will be alone a set. Every option should appear in exactly one set.
|
||||||
|
* WARNING: Has no concept of nested dependencies.
|
||||||
|
* @param fields list of fields for which to check options.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private List<Set<Field>> groupExclusiveOptions( Field[] fields ) {
|
||||||
|
List<Set<Field>> optionGroups = new ArrayList<Set<Field>>();
|
||||||
for(Field field: fields) {
|
for(Field field: fields) {
|
||||||
Argument arg = field.getAnnotation(Argument.class);
|
Argument argument = field.getAnnotation(Argument.class);
|
||||||
if(arg == null)
|
if(argument == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
String fullName = (arg.fullName().length() != 0) ? arg.fullName() : field.getName().trim().toLowerCase();
|
String[] exclusives = argument.exclusive().split(",");
|
||||||
String shortName = (arg.shortName().length() != 0) ? arg.shortName() : fullName.substring(0,1);
|
if( exclusives.length != 0 ) {
|
||||||
|
HashSet<Field> matchingFields = new HashSet<Field>();
|
||||||
|
|
||||||
|
// Find the set of all options exclusive to this one.
|
||||||
|
matchingFields.add( field );
|
||||||
|
for( Field candidate: fields ) {
|
||||||
|
for( String exclusive: exclusives ) {
|
||||||
|
if( candidate.getName().equals( exclusive.trim() ) )
|
||||||
|
matchingFields.add( candidate );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform a set intersection to see whether this set of fields intersects
|
||||||
|
// with any existing set.
|
||||||
|
//
|
||||||
|
// If so, add any additional elements to the list.
|
||||||
|
boolean setExists = false;
|
||||||
|
for( Set<Field> optionGroup: optionGroups ) {
|
||||||
|
Set<Field> working = new HashSet<Field>(optionGroup);
|
||||||
|
working.retainAll( matchingFields );
|
||||||
|
if( working.size() > 0 ) {
|
||||||
|
optionGroup.addAll( matchingFields );
|
||||||
|
setExists = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, add a new option group.
|
||||||
|
if( !setExists )
|
||||||
|
optionGroups.add( matchingFields );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return optionGroups;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a field with some annotations, create a command-line option. If not enough data is
|
||||||
|
* available to create a command-line option, return null.
|
||||||
|
* @param source Source class containing the field.
|
||||||
|
* @param field Field
|
||||||
|
* @return Option representing the field options.
|
||||||
|
*/
|
||||||
|
private Option createOptionFromField( Object source, Field field, Argument argument ) {
|
||||||
|
|
||||||
|
String fullName = (argument.fullName().length() != 0) ? argument.fullName() : field.getName().trim().toLowerCase();
|
||||||
|
String shortName = (argument.shortName().length() != 0) ? argument.shortName() : fullName.substring(0,1);
|
||||||
if(shortName.length() != 1)
|
if(shortName.length() != 1)
|
||||||
throw new IllegalArgumentException("Invalid short name: " + shortName);
|
throw new IllegalArgumentException("Invalid short name: " + shortName);
|
||||||
boolean isFlag = (field.getType() == Boolean.class) || (field.getType() == Boolean.TYPE);
|
boolean isFlag = (field.getType() == Boolean.class) || (field.getType() == Boolean.TYPE);
|
||||||
|
|
@ -352,20 +436,22 @@ public class ArgumentParser {
|
||||||
if( isFlag && isCollection )
|
if( isFlag && isCollection )
|
||||||
throw new IllegalArgumentException("Can't have an array of flags.");
|
throw new IllegalArgumentException("Can't have an array of flags.");
|
||||||
|
|
||||||
String description = arg.doc();
|
String description = String.format("[%s] %s", source, argument.doc());
|
||||||
if( arg.required() )
|
|
||||||
description = (isFlag ? "(Required Flag) " : "(Required Option) ") + description;
|
|
||||||
|
|
||||||
OptionBuilder ob = OptionBuilder.withLongOpt(fullName);
|
OptionBuilder ob = OptionBuilder.withLongOpt(fullName);
|
||||||
if( !isFlag ) ob = ob.withArgName(fullName);
|
if( !isFlag ) {
|
||||||
|
ob = ob.withArgName(fullName);
|
||||||
ob = isCollection ? ob.hasArgs() : ob.hasArg();
|
ob = isCollection ? ob.hasArgs() : ob.hasArg();
|
||||||
if( arg.required() ) ob = ob.isRequired();
|
if( argument.required() ) {
|
||||||
|
ob = ob.isRequired();
|
||||||
|
description = String.format("[%s] (Required Option) %s", source, argument.doc());
|
||||||
|
}
|
||||||
|
}
|
||||||
if( description.length() != 0 ) ob = ob.withDescription( description );
|
if( description.length() != 0 ) ob = ob.withDescription( description );
|
||||||
|
|
||||||
Option option = ob.create( shortName );
|
Option option = ob.create( shortName );
|
||||||
|
|
||||||
AddToOptionStorage(fullName, shortName, new Pair<Object,Field>( source, field ), option );
|
return option;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object constructFromString(Field f, String[] strs) {
|
private Object constructFromString(Field f, String[] strs) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue