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
|
||||
* argument should be independent.
|
||||
*/
|
||||
String exclusive() default "";
|
||||
String exclusiveOf() default "";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -237,7 +237,7 @@ class ArgumentDefinition {
|
|||
/**
|
||||
* Is this argument exclusive of other arguments?
|
||||
*/
|
||||
public final String exclusive;
|
||||
public final String exclusiveOf;
|
||||
|
||||
public final Class sourceClass;
|
||||
public final Field sourceField;
|
||||
|
|
@ -256,7 +256,7 @@ class ArgumentDefinition {
|
|||
shortName = argument.shortName().trim().length() > 0 ? argument.shortName().trim() : null;
|
||||
doc = argument.doc();
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import org.broadinstitute.sting.utils.StingException;
|
||||
import org.broadinstitute.sting.utils.Pair;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
|
@ -112,7 +113,8 @@ public class ParsingEngine {
|
|||
public enum ValidationType { MissingRequiredArgument,
|
||||
InvalidArgument,
|
||||
ValueMissingArgument,
|
||||
TooManyValuesForArgument };
|
||||
TooManyValuesForArgument,
|
||||
MutuallyExclusive };
|
||||
|
||||
/**
|
||||
* Validates the list of command-line argument matches.
|
||||
|
|
@ -157,17 +159,35 @@ public class ParsingEngine {
|
|||
// Find arguments with too many values.
|
||||
if( !skipValidationOf.contains(ValidationType.TooManyValuesForArgument)) {
|
||||
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).
|
||||
if( argumentMatch.definition != null &&
|
||||
!argumentMatch.definition.isMultiValued() &&
|
||||
argumentMatch.values().size() > 1 )
|
||||
if( !argumentMatch.definition.isMultiValued() && argumentMatch.values().size() > 1 )
|
||||
overvaluedArguments.add(argumentMatch);
|
||||
}
|
||||
|
||||
if( !overvaluedArguments.isEmpty() )
|
||||
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 ) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) );
|
||||
}
|
||||
|
||||
@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