diff --git a/java/src/org/broadinstitute/sting/gatk/walkers/Walker.java b/java/src/org/broadinstitute/sting/gatk/walkers/Walker.java index 2e7485a65..e347c944a 100755 --- a/java/src/org/broadinstitute/sting/gatk/walkers/Walker.java +++ b/java/src/org/broadinstitute/sting/gatk/walkers/Walker.java @@ -35,7 +35,6 @@ public abstract class Walker { protected Walker() { if( GenomeAnalysisTK.Instance != null ) { GenomeAnalysisTK gatk = GenomeAnalysisTK.Instance; - gatk.loadArgumentsIntoObject(this); out = new PrintStream( gatk.getOutputTracker().getOutStream() ); err = new PrintStream( gatk.getOutputTracker().getErrStream() ); } diff --git a/java/src/org/broadinstitute/sting/utils/cmdLine/Argument.java b/java/src/org/broadinstitute/sting/utils/cmdLine/Argument.java index 454fc8d56..cd157439d 100755 --- a/java/src/org/broadinstitute/sting/utils/cmdLine/Argument.java +++ b/java/src/org/broadinstitute/sting/utils/cmdLine/Argument.java @@ -59,4 +59,10 @@ public @interface Argument { * argument should be independent. */ String exclusiveOf() default ""; + + /** + * Provide a regexp-based validation string. + * @return Non-empty regexp for validation, blank otherwise. + */ + String validation() default ""; } diff --git a/java/src/org/broadinstitute/sting/utils/cmdLine/ArgumentDefinitions.java b/java/src/org/broadinstitute/sting/utils/cmdLine/ArgumentDefinitions.java index 5323bc1ab..e578c8e16 100755 --- a/java/src/org/broadinstitute/sting/utils/cmdLine/ArgumentDefinitions.java +++ b/java/src/org/broadinstitute/sting/utils/cmdLine/ArgumentDefinitions.java @@ -152,6 +152,12 @@ class ArgumentDefinitions implements Iterable { return definition.required == (Boolean)key; } }; + + static DefinitionMatcher VerifiableDefinitionMatcher = new DefinitionMatcher() { + public boolean matches( ArgumentDefinition definition, Object key ) { + return definition.validation != null; + } + }; } /** @@ -211,6 +217,11 @@ class ArgumentDefinition { */ public final String exclusiveOf; + /** + * Can we validate this regular expression? + */ + public final String validation; + public final Class sourceClass; public final Field sourceField; @@ -229,6 +240,7 @@ class ArgumentDefinition { doc = argument.doc(); required = argument.required() && !isFlag(); exclusiveOf = argument.exclusiveOf().trim().length() > 0 ? argument.exclusiveOf().trim() : null; + validation = argument.validation().trim().length() > 0 ? argument.validation().trim() : null; } /** diff --git a/java/src/org/broadinstitute/sting/utils/cmdLine/ParsingEngine.java b/java/src/org/broadinstitute/sting/utils/cmdLine/ParsingEngine.java index 85dce8a89..944b9009c 100755 --- a/java/src/org/broadinstitute/sting/utils/cmdLine/ParsingEngine.java +++ b/java/src/org/broadinstitute/sting/utils/cmdLine/ParsingEngine.java @@ -108,6 +108,7 @@ public class ParsingEngine { public enum ValidationType { MissingRequiredArgument, InvalidArgument, + InvalidArgumentValue, ValueMissingArgument, TooManyValuesForArgument, MutuallyExclusive }; @@ -146,10 +147,29 @@ public class ParsingEngine { throw new InvalidArgumentException( invalidArguments ); } + // Find invalid argument values (arguments that fail the regexp test. + if( !skipValidationOf.contains(ValidationType.InvalidArgumentValue) ) { + Collection verifiableArguments = + argumentDefinitions.findArgumentDefinitions( null, ArgumentDefinitions.VerifiableDefinitionMatcher ); + Collection> invalidValues = new ArrayList>(); + for( ArgumentDefinition verifiableArgument: verifiableArguments ) { + Collection verifiableMatches = argumentMatches.findMatches( verifiableArgument ); + for( ArgumentMatch verifiableMatch: verifiableMatches ) { + for( String value: verifiableMatch.values() ) { + if( !value.matches(verifiableArgument.validation) ) + invalidValues.add( new Pair(verifiableArgument, value) ); + } + } + } + + if( invalidValues.size() > 0 ) + throw new InvalidArgumentValueException( invalidValues ); + } + // Find values without an associated mate. if( !skipValidationOf.contains(ValidationType.ValueMissingArgument) ) { if( argumentMatches.MissingArgument.values().size() > 0 ) - throw new InvalidArgumentValueException( argumentMatches.MissingArgument ); + throw new UnmatchedArgumentException( argumentMatches.MissingArgument ); } // Find arguments with too many values. @@ -457,10 +477,31 @@ class InvalidArgumentException extends ParseException { } /** - * An exception for values that can't be mated with any argument. + * An exception for values whose format is invalid. */ class InvalidArgumentValueException extends ParseException { - public InvalidArgumentValueException( ArgumentMatch invalidValues ) { + public InvalidArgumentValueException( Collection> invalidArgumentValues ) { + super( formatArguments(invalidArgumentValues) ); + } + + private static String formatArguments( Collection> invalidArgumentValues ) { + StringBuilder sb = new StringBuilder(); + for( Pair invalidValue: invalidArgumentValues ) { + sb.append( String.format("%nArgument '--%s' has value of incorrect format: %s (should match %s)", + invalidValue.first.fullName, + invalidValue.second, + invalidValue.first.validation) ); + } + return sb.toString(); + } +} + + +/** + * An exception for values that can't be mated with any argument. + */ +class UnmatchedArgumentException extends ParseException { + public UnmatchedArgumentException( ArgumentMatch invalidValues ) { super( formatArguments(invalidValues) ); } diff --git a/java/test/org/broadinstitute/sting/utils/cmdLine/ParsingEngineTest.java b/java/test/org/broadinstitute/sting/utils/cmdLine/ParsingEngineTest.java index e2d364f4b..416cb8266 100755 --- a/java/test/org/broadinstitute/sting/utils/cmdLine/ParsingEngineTest.java +++ b/java/test/org/broadinstitute/sting/utils/cmdLine/ParsingEngineTest.java @@ -350,7 +350,7 @@ public class ParsingEngineTest extends BaseTest { public Integer bar; } - @Test(expected=InvalidArgumentValueException.class) + @Test(expected=UnmatchedArgumentException.class) public void missingArgumentNameTest() { final String[] commandLine = new String[] {"foo.txt"}; @@ -363,7 +363,7 @@ public class ParsingEngineTest extends BaseTest { } - @Test(expected=InvalidArgumentValueException.class) + @Test(expected=UnmatchedArgumentException.class) public void extraValueTest() { final String[] commandLine = new String[] {"-Ifoo.txt", "bar.txt"}; @@ -446,7 +446,7 @@ public class ParsingEngineTest extends BaseTest { Integer myArg; } - @Test(expected=InvalidArgumentValueException.class) + @Test(expected=UnmatchedArgumentException.class) public void booleanWithParameterTest() { final String[] commandLine = new String[] {"--mybool", "true"}; @@ -461,7 +461,7 @@ public class ParsingEngineTest extends BaseTest { } @Test - public void testValidParseForAnalysisType() { + public void validParseForAnalysisTypeTest() { final String[] commandLine = new String[] {"--analysis_type", "Pileup" }; parsingEngine.addArgumentSource( AnalysisTypeArgProvider.class ); @@ -480,7 +480,7 @@ public class ParsingEngineTest extends BaseTest { } @Test(expected=TooManyValuesForArgumentException.class) - public void testInvalidParseForAnalysisType() { + public void invalidParseForAnalysisTypeTest() { final String[] commandLine = new String[] {"--analysis_type", "Pileup", "-TCountReads" }; parsingEngine.addArgumentSource( AnalysisTypeArgProvider.class ); @@ -489,7 +489,7 @@ public class ParsingEngineTest extends BaseTest { } @Test(expected=ArgumentsAreMutuallyExclusiveException.class) - public void testMutuallyExclusiveArguments() { + public void mutuallyExclusiveArgumentsTest() { // Passing only foo should work fine... String[] commandLine = new String[] {"--foo","5"}; @@ -516,4 +516,29 @@ public class ParsingEngineTest extends BaseTest { @Argument(doc="bar",required=false) Integer bar; } + + @Test(expected=InvalidArgumentValueException.class) + public void argumentValidationTest() { + // Passing only foo should work fine... + String[] commandLine = new String[] {"--value","521"}; + + parsingEngine.addArgumentSource( ValidatingArgProvider.class ); + parsingEngine.parse( commandLine ); + parsingEngine.validate(); + + ValidatingArgProvider argProvider = new ValidatingArgProvider(); + parsingEngine.loadArgumentsIntoObject( argProvider ); + + Assert.assertEquals("Argument is not correctly initialized", 521, argProvider.value.intValue() ); + + // Try some invalid arguments + commandLine = new String[] {"--value","foo"}; + parsingEngine.parse( commandLine ); + parsingEngine.validate(); + } + + private class ValidatingArgProvider { + @Argument(doc="value",validation="\\d+") + Integer value; + } }