Mutually exclusive options.
git-svn-id: file:///humgen/gsa-scr1/gsa-engineering/svn_contents/trunk@616 348d0f76-0448-11de-a6fe-93d51630548a
This commit is contained in:
parent
752928df94
commit
4177560543
|
|
@ -58,5 +58,5 @@ public @interface Argument {
|
||||||
* @return A comma-separated string listing other arguments of which this
|
* @return A comma-separated string listing other arguments of which this
|
||||||
* argument should be independent.
|
* argument should be independent.
|
||||||
*/
|
*/
|
||||||
String exclusive() default "";
|
String exclusiveOf() default "";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -237,7 +237,7 @@ class ArgumentDefinition {
|
||||||
/**
|
/**
|
||||||
* Is this argument exclusive of other arguments?
|
* Is this argument exclusive of other arguments?
|
||||||
*/
|
*/
|
||||||
public final String exclusive;
|
public final String exclusiveOf;
|
||||||
|
|
||||||
public final Class sourceClass;
|
public final Class sourceClass;
|
||||||
public final Field sourceField;
|
public final Field sourceField;
|
||||||
|
|
@ -256,7 +256,7 @@ class ArgumentDefinition {
|
||||||
shortName = argument.shortName().trim().length() > 0 ? argument.shortName().trim() : null;
|
shortName = argument.shortName().trim().length() > 0 ? argument.shortName().trim() : null;
|
||||||
doc = argument.doc();
|
doc = argument.doc();
|
||||||
required = argument.required() && !isFlag();
|
required = argument.required() && !isFlag();
|
||||||
exclusive = argument.exclusive().trim().length() > 0 ? argument.exclusive().trim() : null;
|
exclusiveOf = argument.exclusiveOf().trim().length() > 0 ? argument.exclusiveOf().trim() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -120,6 +120,17 @@ public class ArgumentMatches implements Iterable<ArgumentMatch> {
|
||||||
return matches;
|
return matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find all successful matches (a 'successful' match is one paired with a definition).
|
||||||
|
*/
|
||||||
|
public Collection<ArgumentMatch> findSuccessfulMatches() {
|
||||||
|
Collection<ArgumentMatch> matches = new HashSet<ArgumentMatch>();
|
||||||
|
for( ArgumentMatch argumentMatch: getUniqueMatches() ) {
|
||||||
|
if( argumentMatch.definition != null )
|
||||||
|
matches.add( argumentMatch );
|
||||||
|
}
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package org.broadinstitute.sting.utils.cmdLine;
|
package org.broadinstitute.sting.utils.cmdLine;
|
||||||
|
|
||||||
import org.broadinstitute.sting.utils.StingException;
|
import org.broadinstitute.sting.utils.StingException;
|
||||||
|
import org.broadinstitute.sting.utils.Pair;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
|
@ -112,7 +113,8 @@ public class ParsingEngine {
|
||||||
public enum ValidationType { MissingRequiredArgument,
|
public enum ValidationType { MissingRequiredArgument,
|
||||||
InvalidArgument,
|
InvalidArgument,
|
||||||
ValueMissingArgument,
|
ValueMissingArgument,
|
||||||
TooManyValuesForArgument };
|
TooManyValuesForArgument,
|
||||||
|
MutuallyExclusive };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the list of command-line argument matches.
|
* Validates the list of command-line argument matches.
|
||||||
|
|
@ -157,17 +159,35 @@ public class ParsingEngine {
|
||||||
// Find arguments with too many values.
|
// Find arguments with too many values.
|
||||||
if( !skipValidationOf.contains(ValidationType.TooManyValuesForArgument)) {
|
if( !skipValidationOf.contains(ValidationType.TooManyValuesForArgument)) {
|
||||||
Collection<ArgumentMatch> overvaluedArguments = new ArrayList<ArgumentMatch>();
|
Collection<ArgumentMatch> overvaluedArguments = new ArrayList<ArgumentMatch>();
|
||||||
for( ArgumentMatch argumentMatch: argumentMatches ) {
|
for( ArgumentMatch argumentMatch: argumentMatches.findSuccessfulMatches() ) {
|
||||||
// Warning: assumes that definition is not null (asserted by checks above).
|
// Warning: assumes that definition is not null (asserted by checks above).
|
||||||
if( argumentMatch.definition != null &&
|
if( !argumentMatch.definition.isMultiValued() && argumentMatch.values().size() > 1 )
|
||||||
!argumentMatch.definition.isMultiValued() &&
|
|
||||||
argumentMatch.values().size() > 1 )
|
|
||||||
overvaluedArguments.add(argumentMatch);
|
overvaluedArguments.add(argumentMatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !overvaluedArguments.isEmpty() )
|
if( !overvaluedArguments.isEmpty() )
|
||||||
throw new TooManyValuesForArgumentException(overvaluedArguments);
|
throw new TooManyValuesForArgumentException(overvaluedArguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find sets of options that are supposed to be mutually exclusive.
|
||||||
|
if( !skipValidationOf.contains(ValidationType.MutuallyExclusive)) {
|
||||||
|
Collection<Pair<ArgumentMatch,ArgumentMatch>> invalidPairs = new ArrayList<Pair<ArgumentMatch,ArgumentMatch>>();
|
||||||
|
for( ArgumentMatch argumentMatch: argumentMatches.findSuccessfulMatches() ) {
|
||||||
|
if( argumentMatch.definition.exclusiveOf != null ) {
|
||||||
|
for( ArgumentMatch conflictingMatch: argumentMatches.findSuccessfulMatches() ) {
|
||||||
|
// Skip over the current element.
|
||||||
|
if( argumentMatch == conflictingMatch )
|
||||||
|
continue;
|
||||||
|
if( argumentMatch.definition.exclusiveOf.equals(conflictingMatch.definition.fullName) ||
|
||||||
|
argumentMatch.definition.exclusiveOf.equals(conflictingMatch.definition.shortName))
|
||||||
|
invalidPairs.add( new Pair<ArgumentMatch,ArgumentMatch>(argumentMatch, conflictingMatch) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !invalidPairs.isEmpty() )
|
||||||
|
throw new ArgumentsAreMutuallyExclusiveException( invalidPairs );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -537,7 +557,24 @@ class TooManyValuesForArgumentException extends ParseException {
|
||||||
private static String formatArguments( Collection<ArgumentMatch> arguments ) {
|
private static String formatArguments( Collection<ArgumentMatch> arguments ) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
for( ArgumentMatch argument: arguments )
|
for( ArgumentMatch argument: arguments )
|
||||||
sb.append( String.format("Argument with name '%s' has to many values: %s", argument.label, Arrays.deepToString(argument.values().toArray())) );
|
sb.append( String.format("Argument '%s' has to many values: %s", argument.label, Arrays.deepToString(argument.values().toArray())) );
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception indicating that mutually exclusive options have been passed in the same command line.
|
||||||
|
*/
|
||||||
|
class ArgumentsAreMutuallyExclusiveException extends ParseException {
|
||||||
|
public ArgumentsAreMutuallyExclusiveException( Collection<Pair<ArgumentMatch,ArgumentMatch>> arguments ) {
|
||||||
|
super( formatArguments(arguments) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String formatArguments( Collection<Pair<ArgumentMatch,ArgumentMatch>> arguments ) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for( Pair<ArgumentMatch,ArgumentMatch> argument: arguments )
|
||||||
|
sb.append( String.format("Arguments '%s' and '%s' are mutually exclusive.", argument.first.definition.fullName, argument.second.definition.fullName ) );
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -425,4 +425,32 @@ public class ParsingEngineTest extends BaseTest {
|
||||||
parsingEngine.validate( EnumSet.of(ParsingEngine.ValidationType.MissingRequiredArgument) );
|
parsingEngine.validate( EnumSet.of(ParsingEngine.ValidationType.MissingRequiredArgument) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected=ArgumentsAreMutuallyExclusiveException.class)
|
||||||
|
public void testMutuallyExclusiveArguments() {
|
||||||
|
// Passing only foo should work fine...
|
||||||
|
String[] commandLine = new String[] {"--foo","5"};
|
||||||
|
|
||||||
|
parsingEngine.addArgumentSource( MutuallyExclusiveArgProvider.class );
|
||||||
|
parsingEngine.parse( commandLine );
|
||||||
|
parsingEngine.validate();
|
||||||
|
|
||||||
|
MutuallyExclusiveArgProvider argProvider = new MutuallyExclusiveArgProvider();
|
||||||
|
parsingEngine.loadArgumentsIntoObject( argProvider );
|
||||||
|
|
||||||
|
Assert.assertEquals("Argument is not correctly initialized", 5, argProvider.foo.intValue() );
|
||||||
|
|
||||||
|
// But when foo and bar come together, danger!
|
||||||
|
commandLine = new String[] {"--foo","5","--bar","6"};
|
||||||
|
|
||||||
|
parsingEngine.parse( commandLine );
|
||||||
|
parsingEngine.validate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MutuallyExclusiveArgProvider {
|
||||||
|
@Argument(doc="foo",exclusiveOf="bar")
|
||||||
|
Integer foo;
|
||||||
|
|
||||||
|
@Argument(doc="bar",required=false)
|
||||||
|
Integer bar;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue