2010-04-20 07:00:08 +08:00
/ *
* Copyright ( c ) 2010 The Broad Institute
2010-04-20 23:26:32 +08:00
*
2010-04-20 07:00:08 +08:00
* Permission is hereby granted , free of charge , to any person
* obtaining a copy of this software and associated documentation
2010-04-20 23:26:32 +08:00
* files ( the "Software" ) , to deal in the Software without
2010-04-20 07:00:08 +08:00
* restriction , including without limitation the rights to use ,
* copy , modify , merge , publish , distribute , sublicense , and / or sell
* copies of the Software , and to permit persons to whom the
* Software is furnished to do so , subject to the following
* conditions :
2010-04-20 23:26:32 +08:00
*
2010-04-20 07:00:08 +08:00
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software .
*
2010-04-20 23:26:32 +08:00
* THE SOFTWARE IS PROVIDED "AS IS" , WITHOUT WARRANTY OF ANY KIND ,
2010-04-20 07:00:08 +08:00
* EXPRESS OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT . IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER LIABILITY ,
* WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING
* FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
* THE USE OR OTHER DEALINGS IN THE SOFTWARE .
* /
package org.broadinstitute.sting.commandline ;
2009-05-04 08:11:42 +08:00
2011-08-08 08:16:51 +08:00
import com.google.java.contract.Requires ;
2011-10-15 00:06:41 +08:00
import org.apache.commons.io.FileUtils ;
2011-07-18 08:29:58 +08:00
import org.apache.log4j.Logger ;
2010-01-21 05:36:46 +08:00
import org.broadinstitute.sting.utils.Utils ;
2011-07-18 08:29:58 +08:00
import org.broadinstitute.sting.utils.classloader.JVMUtils ;
import org.broadinstitute.sting.utils.collections.Pair ;
import org.broadinstitute.sting.utils.exceptions.ReviewedStingException ;
2010-09-12 22:02:43 +08:00
import org.broadinstitute.sting.utils.exceptions.UserException ;
2009-12-08 03:23:12 +08:00
import org.broadinstitute.sting.utils.help.ApplicationDetails ;
import org.broadinstitute.sting.utils.help.HelpFormatter ;
2009-05-04 08:11:42 +08:00
2011-10-15 00:06:41 +08:00
import java.io.File ;
import java.io.IOException ;
2011-07-18 08:29:58 +08:00
import java.lang.reflect.Field ;
2009-07-13 09:42:13 +08:00
import java.util.* ;
2009-05-04 08:11:42 +08:00
/ * *
* A parser for Sting command - line arguments .
* /
public class ParsingEngine {
2011-07-26 01:20:00 +08:00
/ * *
* The loaded argument sources along with their back definitions .
* /
private Map < ArgumentDefinition , ArgumentSource > argumentSourcesByDefinition = new HashMap < ArgumentDefinition , ArgumentSource > ( ) ;
2009-05-04 08:11:42 +08:00
/ * *
* A list of defined arguments against which command lines are matched .
2009-05-06 06:08:00 +08:00
* Package protected for testing access .
2009-05-04 08:11:42 +08:00
* /
2011-07-21 12:18:07 +08:00
public ArgumentDefinitions argumentDefinitions = new ArgumentDefinitions ( ) ;
2009-05-04 08:11:42 +08:00
2009-05-07 03:38:05 +08:00
/ * *
* A list of matches from defined arguments to command - line text .
* Indicates as best as possible where command - line text remains unmatched
* to existing arguments .
* /
ArgumentMatches argumentMatches = null ;
2009-05-05 06:41:23 +08:00
/ * *
* Techniques for parsing and for argument lookup .
* /
private List < ParsingMethod > parsingMethods = new ArrayList < ParsingMethod > ( ) ;
2011-07-27 23:36:53 +08:00
/ * *
* All of the RodBinding objects we ' ve seen while parsing
* /
private List < RodBinding > rodBindings = new ArrayList < RodBinding > ( ) ;
2010-10-28 03:44:55 +08:00
/ * *
* Class reference to the different types of descriptors that the create method can create .
* The type of set used must be ordered ( but not necessarily sorted ) .
* /
private static final Set < ArgumentTypeDescriptor > STANDARD_ARGUMENT_TYPE_DESCRIPTORS = new LinkedHashSet < ArgumentTypeDescriptor > ( Arrays . asList ( new SimpleArgumentTypeDescriptor ( ) ,
2011-10-27 02:11:49 +08:00
new IntervalBindingArgumentTypeDescriptor ( ) ,
2011-07-26 23:09:06 +08:00
new RodBindingArgumentTypeDescriptor ( ) ,
2010-10-28 03:44:55 +08:00
new CompoundArgumentTypeDescriptor ( ) ,
new MultiplexArgumentTypeDescriptor ( ) ) ) ;
private Set < ArgumentTypeDescriptor > argumentTypeDescriptors = new LinkedHashSet < ArgumentTypeDescriptor > ( ) ;
2010-10-29 02:37:42 +08:00
/ * *
* List of tags associated with the given instantiation of the command - line argument .
* /
2011-01-21 08:22:42 +08:00
private final Map < Object , Tags > tags = new IdentityHashMap < Object , Tags > ( ) ;
2010-10-29 02:37:42 +08:00
2009-05-05 06:41:23 +08:00
/ * *
* our log , which we want to capture anything from org . broadinstitute . sting
* /
2009-05-07 02:37:51 +08:00
protected static Logger logger = Logger . getLogger ( ParsingEngine . class ) ;
2009-05-05 06:41:23 +08:00
2009-07-30 00:11:45 +08:00
public ParsingEngine ( CommandLineProgram clp ) {
2011-08-05 00:06:10 +08:00
RodBinding . resetNameCounter ( ) ;
2009-05-18 00:09:32 +08:00
parsingMethods . add ( ParsingMethod . FullNameParsingMethod ) ;
parsingMethods . add ( ParsingMethod . ShortNameParsingMethod ) ;
2009-08-23 08:56:02 +08:00
2010-10-28 03:44:55 +08:00
// Order matters here! Make sure the clp's new type descriptors go in before the original type descriptors.
if ( clp ! = null )
argumentTypeDescriptors . addAll ( clp . getArgumentTypeDescriptors ( ) ) ;
argumentTypeDescriptors . addAll ( STANDARD_ARGUMENT_TYPE_DESCRIPTORS ) ;
2011-10-15 00:06:41 +08:00
addArgumentSource ( ParsingEngineArgumentFiles . class ) ;
2009-05-05 06:41:23 +08:00
}
2009-05-04 08:11:42 +08:00
/ * *
2009-05-07 05:16:01 +08:00
* Add a main argument source . Argument sources are expected to have
2009-05-04 08:11:42 +08:00
* any number of fields with an @Argument annotation attached .
2009-05-07 05:16:01 +08:00
* @param source An argument source from which to extract command - line arguments .
2009-05-04 08:11:42 +08:00
* /
2009-05-07 03:38:05 +08:00
public void addArgumentSource ( Class source ) {
2009-05-07 05:16:01 +08:00
addArgumentSource ( null , source ) ;
}
/ * *
* Add an argument source . Argument sources are expected to have
* any number of fields with an @Argument annotation attached .
* @param sourceName name for this argument source . ' Null ' indicates that this source should be treated
* as the main module .
2009-07-13 09:42:13 +08:00
* @param sourceClass A class containing argument sources from which to extract command - line arguments .
2009-05-07 05:16:01 +08:00
* /
2009-07-13 09:42:13 +08:00
public void addArgumentSource ( String sourceName , Class sourceClass ) {
2009-05-21 03:01:25 +08:00
List < ArgumentDefinition > argumentsFromSource = new ArrayList < ArgumentDefinition > ( ) ;
2011-07-26 01:20:00 +08:00
for ( ArgumentSource argumentSource : extractArgumentSources ( sourceClass ) ) {
List < ArgumentDefinition > argumentDefinitions = argumentSource . createArgumentDefinitions ( ) ;
for ( ArgumentDefinition argumentDefinition : argumentDefinitions ) {
argumentSourcesByDefinition . put ( argumentDefinition , argumentSource ) ;
argumentsFromSource . add ( argumentDefinition ) ;
}
}
2009-05-07 05:16:01 +08:00
argumentDefinitions . add ( new ArgumentDefinitionGroup ( sourceName , argumentsFromSource ) ) ;
2009-05-04 08:11:42 +08:00
}
2009-05-18 08:42:00 +08:00
/ * *
* Do a cursory search to see if an argument with the given name is present .
* @param argumentFullName full name of the argument .
* @return True if the argument is present . False otherwise .
* /
public boolean isArgumentPresent ( String argumentFullName ) {
ArgumentDefinition definition =
argumentDefinitions . findArgumentDefinition ( argumentFullName , ArgumentDefinitions . FullNameDefinitionMatcher ) ;
return argumentMatches . hasMatch ( definition ) ;
}
2009-05-04 08:11:42 +08:00
/ * *
* Parse the given set of command - line arguments , returning
* an ArgumentMatches object describing the best fit of these
* command - line arguments to the arguments that are actually
* required .
2009-05-05 06:41:23 +08:00
* @param tokens Tokens passed on the command line .
2011-10-15 00:06:41 +08:00
* @return The parsed arguments by file .
2009-05-04 08:11:42 +08:00
* /
2011-10-15 00:06:41 +08:00
public SortedMap < ArgumentMatchSource , List < String > > parse ( String [ ] tokens ) {
2009-07-13 09:42:13 +08:00
argumentMatches = new ArgumentMatches ( ) ;
2011-10-15 00:06:41 +08:00
SortedMap < ArgumentMatchSource , List < String > > parsedArgs = new TreeMap < ArgumentMatchSource , List < String > > ( ) ;
List < String > cmdLineTokens = Arrays . asList ( tokens ) ;
parse ( ArgumentMatchSource . COMMAND_LINE , cmdLineTokens , argumentMatches , parsedArgs ) ;
ParsingEngineArgumentFiles argumentFiles = new ParsingEngineArgumentFiles ( ) ;
2009-07-13 09:42:13 +08:00
2011-10-15 00:06:41 +08:00
// Load the arguments ONLY into the argument files.
// Validation may optionally run on the rest of the arguments.
loadArgumentsIntoObject ( argumentFiles ) ;
2009-07-13 09:42:13 +08:00
2011-10-15 00:06:41 +08:00
for ( File file : argumentFiles . files ) {
List < String > fileTokens = getArguments ( file ) ;
parse ( new ArgumentMatchSource ( file ) , fileTokens , argumentMatches , parsedArgs ) ;
}
return parsedArgs ;
}
private void parse ( ArgumentMatchSource matchSource , List < String > tokens ,
ArgumentMatches argumentMatches , SortedMap < ArgumentMatchSource , List < String > > parsedArgs ) {
ArgumentMatchSite lastArgumentMatchSite = new ArgumentMatchSite ( matchSource , - 1 ) ;
int i = 0 ;
for ( String token : tokens ) {
2009-07-13 09:42:13 +08:00
// If the token is of argument form, parse it into its own argument match.
// Otherwise, pair it with the most recently used argument discovered.
2011-10-15 00:06:41 +08:00
ArgumentMatchSite site = new ArgumentMatchSite ( matchSource , i ) ;
2009-07-13 09:42:13 +08:00
if ( isArgumentForm ( token ) ) {
2011-10-15 00:06:41 +08:00
ArgumentMatch argumentMatch = parseArgument ( token , site ) ;
2009-07-13 09:42:13 +08:00
if ( argumentMatch ! = null ) {
argumentMatches . mergeInto ( argumentMatch ) ;
2011-10-15 00:06:41 +08:00
lastArgumentMatchSite = site ;
2009-07-13 09:42:13 +08:00
}
}
else {
if ( argumentMatches . hasMatch ( lastArgumentMatchSite ) & &
! argumentMatches . getMatch ( lastArgumentMatchSite ) . hasValueAtSite ( lastArgumentMatchSite ) )
argumentMatches . getMatch ( lastArgumentMatchSite ) . addValue ( lastArgumentMatchSite , token ) ;
else
2011-10-15 00:06:41 +08:00
argumentMatches . MissingArgument . addValue ( site , token ) ;
2009-07-13 09:42:13 +08:00
}
2011-10-15 00:06:41 +08:00
i + + ;
2009-07-13 09:42:13 +08:00
}
2011-10-15 00:06:41 +08:00
parsedArgs . put ( matchSource , tokens ) ;
}
private List < String > getArguments ( File file ) {
try {
if ( file . getAbsolutePath ( ) . endsWith ( ".list" ) ) {
return getListArguments ( file ) ;
}
} catch ( IOException e ) {
throw new UserException . CouldNotReadInputFile ( file , e ) ;
}
throw new UserException . CouldNotReadInputFile ( file , "file extension is not .list" ) ;
}
private List < String > getListArguments ( File file ) throws IOException {
ArrayList < String > argsList = new ArrayList < String > ( ) ;
for ( String line : FileUtils . readLines ( file ) )
argsList . addAll ( Arrays . asList ( Utils . escapeExpressions ( line ) ) ) ;
return argsList ;
2009-05-04 08:11:42 +08:00
}
2009-05-06 21:38:46 +08:00
public enum ValidationType { MissingRequiredArgument ,
InvalidArgument ,
2009-05-08 04:28:56 +08:00
InvalidArgumentValue ,
2009-05-06 21:38:46 +08:00
ValueMissingArgument ,
2009-05-07 21:27:48 +08:00
TooManyValuesForArgument ,
2009-07-13 09:42:13 +08:00
MutuallyExclusive }
2009-05-06 21:38:46 +08:00
2009-05-04 08:11:42 +08:00
/ * *
2009-05-06 21:38:46 +08:00
* Validates the list of command - line argument matches .
2009-05-04 08:11:42 +08:00
* /
2009-05-07 03:38:05 +08:00
public void validate ( ) {
validate ( EnumSet . noneOf ( ValidationType . class ) ) ;
2009-05-06 21:38:46 +08:00
}
/ * *
* Validates the list of command - line argument matches . On failure throws an exception with detailed info about the
* particular failures . Takes an EnumSet indicating which validation checks to skip .
* @param skipValidationOf List of validation checks to skip .
* /
2009-05-07 03:38:05 +08:00
public void validate ( EnumSet < ValidationType > skipValidationOf ) {
2009-05-06 06:08:00 +08:00
// Find missing required arguments.
2009-05-06 21:38:46 +08:00
if ( ! skipValidationOf . contains ( ValidationType . MissingRequiredArgument ) ) {
Collection < ArgumentDefinition > requiredArguments =
argumentDefinitions . findArgumentDefinitions ( true , ArgumentDefinitions . RequiredDefinitionMatcher ) ;
Collection < ArgumentDefinition > missingArguments = new ArrayList < ArgumentDefinition > ( ) ;
for ( ArgumentDefinition requiredArgument : requiredArguments ) {
if ( ! argumentMatches . hasMatch ( requiredArgument ) )
missingArguments . add ( requiredArgument ) ;
}
2009-05-06 06:08:00 +08:00
2009-05-06 21:38:46 +08:00
if ( missingArguments . size ( ) > 0 )
throw new MissingArgumentException ( missingArguments ) ;
}
2009-05-06 06:08:00 +08:00
// Find invalid arguments. Invalid arguments will have a null argument definition.
2009-05-06 21:38:46 +08:00
if ( ! skipValidationOf . contains ( ValidationType . InvalidArgument ) ) {
2009-08-23 08:56:02 +08:00
ArgumentMatches invalidArguments = argumentMatches . findUnmatched ( ) ;
2009-05-06 21:38:46 +08:00
if ( invalidArguments . size ( ) > 0 )
throw new InvalidArgumentException ( invalidArguments ) ;
}
2009-05-06 06:08:00 +08:00
2011-07-26 01:20:00 +08:00
// Find invalid argument values -- invalid arguments are either completely missing or fail the specified 'validation' regular expression.
2009-05-08 04:28:56 +08:00
if ( ! skipValidationOf . contains ( ValidationType . InvalidArgumentValue ) ) {
Collection < ArgumentDefinition > verifiableArguments =
argumentDefinitions . findArgumentDefinitions ( null , ArgumentDefinitions . VerifiableDefinitionMatcher ) ;
Collection < Pair < ArgumentDefinition , String > > invalidValues = new ArrayList < Pair < ArgumentDefinition , String > > ( ) ;
for ( ArgumentDefinition verifiableArgument : verifiableArguments ) {
2009-08-23 08:56:02 +08:00
ArgumentMatches verifiableMatches = argumentMatches . findMatches ( verifiableArgument ) ;
2011-07-26 01:20:00 +08:00
// Check to see whether an argument value was specified. Argument values must be provided
// when the argument name is specified and the argument is not a flag type.
for ( ArgumentMatch verifiableMatch : verifiableMatches ) {
ArgumentSource argumentSource = argumentSourcesByDefinition . get ( verifiableArgument ) ;
if ( verifiableMatch . values ( ) . size ( ) = = 0 & & ! verifiableArgument . isFlag & & argumentSource . createsTypeDefault ( ) )
invalidValues . add ( new Pair < ArgumentDefinition , String > ( verifiableArgument , null ) ) ;
}
// Ensure that the field contents meet the validation criteria specified by the regular expression.
2009-05-08 04:28:56 +08:00
for ( ArgumentMatch verifiableMatch : verifiableMatches ) {
for ( String value : verifiableMatch . values ( ) ) {
2011-07-26 01:20:00 +08:00
if ( verifiableArgument . validation ! = null & & ! value . matches ( verifiableArgument . validation ) )
2009-05-08 04:28:56 +08:00
invalidValues . add ( new Pair < ArgumentDefinition , String > ( verifiableArgument , value ) ) ;
}
}
}
if ( invalidValues . size ( ) > 0 )
throw new InvalidArgumentValueException ( invalidValues ) ;
}
2009-05-06 06:08:00 +08:00
// Find values without an associated mate.
2009-05-06 21:38:46 +08:00
if ( ! skipValidationOf . contains ( ValidationType . ValueMissingArgument ) ) {
if ( argumentMatches . MissingArgument . values ( ) . size ( ) > 0 )
2009-05-08 04:28:56 +08:00
throw new UnmatchedArgumentException ( argumentMatches . MissingArgument ) ;
2009-05-06 21:38:46 +08:00
}
2009-05-06 06:08:00 +08:00
// Find arguments with too many values.
2009-05-06 21:38:46 +08:00
if ( ! skipValidationOf . contains ( ValidationType . TooManyValuesForArgument ) ) {
Collection < ArgumentMatch > overvaluedArguments = new ArrayList < ArgumentMatch > ( ) ;
2009-05-07 21:27:48 +08:00
for ( ArgumentMatch argumentMatch : argumentMatches . findSuccessfulMatches ( ) ) {
2009-05-06 21:38:46 +08:00
// Warning: assumes that definition is not null (asserted by checks above).
2009-12-21 08:39:36 +08:00
if ( ! argumentMatch . definition . isMultiValued & & argumentMatch . values ( ) . size ( ) > 1 )
2009-05-06 21:38:46 +08:00
overvaluedArguments . add ( argumentMatch ) ;
}
2009-05-06 06:08:00 +08:00
2009-05-06 21:38:46 +08:00
if ( ! overvaluedArguments . isEmpty ( ) )
throw new TooManyValuesForArgumentException ( overvaluedArguments ) ;
}
2009-05-07 21:27:48 +08:00
// 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 ) ;
}
2009-05-04 08:11:42 +08:00
}
/ * *
* Loads a set of matched command - line arguments into the given object .
* @param object Object into which to add arguments .
* /
2009-05-07 03:38:05 +08:00
public void loadArgumentsIntoObject ( Object object ) {
2009-09-30 06:23:19 +08:00
List < ArgumentSource > argumentSources = extractArgumentSources ( object . getClass ( ) ) ;
2010-08-22 22:27:05 +08:00
List < ArgumentSource > dependentArguments = new ArrayList < ArgumentSource > ( ) ;
2010-08-23 05:01:44 +08:00
2010-08-22 22:27:05 +08:00
for ( ArgumentSource argumentSource : argumentSources ) {
2010-10-28 03:44:55 +08:00
if ( argumentSource . isDeprecated ( ) & & argumentMatches . findMatches ( this , argumentSource ) . size ( ) > 0 )
2010-08-23 05:01:44 +08:00
notifyDeprecatedCommandLineArgument ( argumentSource ) ;
2010-08-22 22:27:05 +08:00
// If this argument source depends on other command-line arguments, skip it and make a note to process it later.
if ( argumentSource . isDependent ( ) ) {
dependentArguments . add ( argumentSource ) ;
continue ;
}
2010-10-28 03:44:55 +08:00
loadValueIntoObject ( argumentSource , object , argumentMatches . findMatches ( this , argumentSource ) ) ;
2010-08-22 22:27:05 +08:00
}
for ( ArgumentSource dependentArgument : dependentArguments ) {
2010-10-28 03:44:55 +08:00
MultiplexArgumentTypeDescriptor dependentDescriptor = dependentArgument . createDependentTypeDescriptor ( this , object ) ;
2010-08-22 22:27:05 +08:00
ArgumentSource dependentSource = dependentArgument . copyWithCustomTypeDescriptor ( dependentDescriptor ) ;
2010-10-28 03:44:55 +08:00
loadValueIntoObject ( dependentSource , object , argumentMatches . findMatches ( this , dependentSource ) ) ;
2010-08-22 22:27:05 +08:00
}
2009-05-18 08:42:00 +08:00
}
2010-08-25 11:47:57 +08:00
/ * *
* Notify the user that tags have been created .
* @param key The key created .
* @param tags List of tags , or empty list if no tags are present .
* /
2011-01-21 08:22:42 +08:00
public void addTags ( Object key , final Tags tags ) {
2010-10-29 02:37:42 +08:00
this . tags . put ( key , tags ) ;
2010-08-25 11:47:57 +08:00
}
2010-10-29 02:37:42 +08:00
/ * *
* Gets the tags associated with a given object .
* @param key Key for which to find a tag .
* @return List of tags associated with this key .
* /
2011-01-21 08:22:42 +08:00
public Tags getTags ( Object key ) {
2010-10-29 02:37:42 +08:00
if ( ! tags . containsKey ( key ) )
2011-01-21 08:22:42 +08:00
return new Tags ( ) ;
2010-10-29 02:37:42 +08:00
return tags . get ( key ) ;
2011-08-08 08:16:51 +08:00
}
/ * *
* Add a RodBinding type argument to this parser . Called during parsing to allow
* us to track all of the RodBindings discovered in the command line .
* @param rodBinding the rodbinding to add . Must not be added twice
* /
@Requires ( "rodBinding != null" )
public void addRodBinding ( final RodBinding rodBinding ) {
rodBindings . add ( rodBinding ) ;
}
2010-10-29 02:37:42 +08:00
2010-08-23 05:01:44 +08:00
/ * *
* Notify the user that a deprecated command - line argument has been used .
* @param argumentSource Deprecated argument source specified by user .
* /
private void notifyDeprecatedCommandLineArgument ( ArgumentSource argumentSource ) {
// Grab the first argument definition and report that one as the failure. Theoretically, we should notify of all failures.
List < ArgumentDefinition > definitions = argumentSource . createArgumentDefinitions ( ) ;
if ( definitions . size ( ) < 1 )
2010-09-12 23:07:38 +08:00
throw new ReviewedStingException ( "Internal error. Argument source creates no definitions." ) ;
2010-08-23 05:01:44 +08:00
ArgumentDefinition definition = definitions . get ( 0 ) ;
2010-09-12 22:02:43 +08:00
throw new UserException . DeprecatedArgument ( definition . fullName , definition . doc ) ;
2010-08-23 05:01:44 +08:00
}
2009-05-18 08:42:00 +08:00
/ * *
2009-09-30 06:23:19 +08:00
* Loads a single argument into the object and that objects children .
2009-07-13 09:42:13 +08:00
* @param argumentMatches Argument matches to load into the object .
2009-09-30 06:23:19 +08:00
* @param source Argument source to load into the object .
* @param instance Object into which to inject the value . The target might be in a container within the instance .
2009-05-18 08:42:00 +08:00
* /
2009-12-30 00:46:24 +08:00
private void loadValueIntoObject ( ArgumentSource source , Object instance , ArgumentMatches argumentMatches ) {
2009-07-30 00:11:45 +08:00
// Nothing to load
2011-08-11 20:58:30 +08:00
if ( argumentMatches . size ( ) = = 0 & & ! source . createsTypeDefault ( ) )
2009-05-18 08:42:00 +08:00
return ;
2009-09-30 06:23:19 +08:00
// Target instance into which to inject the value.
2010-08-10 00:42:48 +08:00
Collection < Object > targets = findTargets ( source , instance ) ;
2009-09-30 06:23:19 +08:00
// Abort if no home is found for the object.
if ( targets . size ( ) = = 0 )
2010-09-12 23:07:38 +08:00
throw new ReviewedStingException ( "Internal command-line parser error: unable to find a home for argument matches " + argumentMatches ) ;
2009-09-30 06:23:19 +08:00
for ( Object target : targets ) {
2010-12-15 03:35:15 +08:00
Object value = ( argumentMatches . size ( ) ! = 0 ) ? source . parse ( this , argumentMatches ) : source . createTypeDefault ( this ) ;
2010-08-25 11:47:57 +08:00
2009-12-30 00:46:24 +08:00
JVMUtils . setFieldValue ( source . field , target , value ) ;
2009-05-04 08:11:42 +08:00
}
}
2009-05-05 06:41:23 +08:00
2011-07-27 23:36:53 +08:00
public Collection < RodBinding > getRodBindings ( ) {
return Collections . unmodifiableCollection ( rodBindings ) ;
}
2010-08-10 00:42:48 +08:00
/ * *
* Gets a collection of the container instances of the given type stored within the given target .
* @param source Argument source .
* @param instance Container .
* @return A collection of containers matching the given argument source .
* /
private Collection < Object > findTargets ( ArgumentSource source , Object instance ) {
LinkedHashSet < Object > targets = new LinkedHashSet < Object > ( ) ;
for ( Class clazz = instance . getClass ( ) ; clazz ! = null ; clazz = clazz . getSuperclass ( ) ) {
for ( Field field : clazz . getDeclaredFields ( ) ) {
if ( field . equals ( source . field ) ) {
targets . add ( instance ) ;
} else if ( field . isAnnotationPresent ( ArgumentCollection . class ) ) {
targets . addAll ( findTargets ( source , JVMUtils . getFieldValue ( field , instance ) ) ) ;
}
}
}
return targets ;
}
2009-05-07 02:16:11 +08:00
/ * *
* Prints out the help associated with these command - line argument definitions .
2009-09-30 06:23:19 +08:00
* @param applicationDetails Details about the specific GATK - based application being run .
2009-05-07 02:16:11 +08:00
* /
2009-07-22 06:23:28 +08:00
public void printHelp ( ApplicationDetails applicationDetails ) {
new HelpFormatter ( ) . printHelp ( applicationDetails , argumentDefinitions ) ;
2009-05-07 02:16:11 +08:00
}
2009-05-06 06:08:00 +08:00
/ * *
2009-07-13 09:42:13 +08:00
* Extract all the argument sources from a given object .
* @param sourceClass class to act as sources for other arguments .
* @return A list of sources associated with this object and its aggregated objects .
2009-05-06 06:08:00 +08:00
* /
2010-10-28 03:44:55 +08:00
public List < ArgumentSource > extractArgumentSources ( Class sourceClass ) {
2010-08-10 00:42:48 +08:00
return extractArgumentSources ( sourceClass , new Field [ 0 ] ) ;
}
2010-10-28 03:44:55 +08:00
/ * *
* Fetch the best command - line argument descriptor for the given class .
* @param type Class for which to specify a descriptor .
* @return descriptor for the given type .
* /
public ArgumentTypeDescriptor selectBestTypeDescriptor ( Class type ) {
return ArgumentTypeDescriptor . selectBest ( argumentTypeDescriptors , type ) ;
}
private List < ArgumentSource > extractArgumentSources ( Class sourceClass , Field [ ] parentFields ) {
2010-08-29 06:53:32 +08:00
// now simply call into the truly general routine extract argument bindings but with a null
// object so bindings aren't computed
Map < ArgumentSource , Object > bindings = extractArgumentBindings ( null , sourceClass , parentFields ) ;
return new ArrayList < ArgumentSource > ( bindings . keySet ( ) ) ;
}
2010-10-28 03:44:55 +08:00
public Map < ArgumentSource , Object > extractArgumentBindings ( Object obj ) {
2010-08-29 06:53:32 +08:00
if ( obj = = null ) throw new IllegalArgumentException ( "Incoming object cannot be null" ) ;
return extractArgumentBindings ( obj , obj . getClass ( ) , new Field [ 0 ] ) ;
}
/ * *
* Extract all the argument sources from a given object , along with their bindings if obj ! = null .
* @param obj the object corresponding to the sourceClass
* @param sourceClass class to act as sources for other arguments .
2010-10-21 05:43:52 +08:00
* @param parentFields Parent Fields
2010-08-29 06:53:32 +08:00
* @return A map of sources associated with this object and its aggregated objects and bindings to their bindings values
* /
2010-10-28 03:44:55 +08:00
private Map < ArgumentSource , Object > extractArgumentBindings ( Object obj , Class sourceClass , Field [ ] parentFields ) {
2010-10-21 05:43:52 +08:00
Map < ArgumentSource , Object > bindings = new LinkedHashMap < ArgumentSource , Object > ( ) ;
2010-08-29 06:53:32 +08:00
2009-07-13 09:42:13 +08:00
while ( sourceClass ! = null ) {
Field [ ] fields = sourceClass . getDeclaredFields ( ) ;
for ( Field field : fields ) {
2010-08-29 06:53:32 +08:00
if ( ArgumentTypeDescriptor . isArgumentAnnotationPresent ( field ) ) {
Object val = obj ! = null ? JVMUtils . getFieldValue ( field , obj ) : null ;
2010-10-28 03:44:55 +08:00
bindings . put ( new ArgumentSource ( parentFields , field , selectBestTypeDescriptor ( field . getType ( ) ) ) , val ) ;
2010-08-29 06:53:32 +08:00
}
2010-08-10 00:42:48 +08:00
if ( field . isAnnotationPresent ( ArgumentCollection . class ) ) {
2010-08-29 06:53:32 +08:00
Object val = obj ! = null ? JVMUtils . getFieldValue ( field , obj ) : null ;
2010-08-10 00:42:48 +08:00
Field [ ] newParentFields = Arrays . copyOf ( parentFields , parentFields . length + 1 ) ;
newParentFields [ parentFields . length ] = field ;
2010-08-29 06:53:32 +08:00
bindings . putAll ( extractArgumentBindings ( val , field . getType ( ) , newParentFields ) ) ;
2010-08-10 00:42:48 +08:00
}
2009-07-13 09:42:13 +08:00
}
2010-08-29 06:53:32 +08:00
2009-07-13 09:42:13 +08:00
sourceClass = sourceClass . getSuperclass ( ) ;
}
2010-08-29 06:53:32 +08:00
return bindings ;
}
2009-05-05 06:41:23 +08:00
/ * *
* Determines whether a token looks like the name of an argument .
* @param token Token to inspect . Can be surrounded by whitespace .
* @return True if token is of short name form .
* /
private boolean isArgumentForm ( String token ) {
for ( ParsingMethod parsingMethod : parsingMethods ) {
2009-05-08 00:21:17 +08:00
if ( parsingMethod . matches ( token ) )
2009-05-05 06:41:23 +08:00
return true ;
}
return false ;
}
/ * *
* Parse a short name into an ArgumentMatch .
* @param token The token to parse . The token should pass the isLongArgumentForm test .
2009-09-30 06:23:19 +08:00
* @param position The position of the token in question .
2009-05-05 06:41:23 +08:00
* @return ArgumentMatch associated with this token , or null if no match exists .
* /
2011-10-15 00:06:41 +08:00
private ArgumentMatch parseArgument ( String token , ArgumentMatchSite position ) {
2009-05-05 06:41:23 +08:00
if ( ! isArgumentForm ( token ) )
throw new IllegalArgumentException ( "Token is not recognizable as an argument: " + token ) ;
for ( ParsingMethod parsingMethod : parsingMethods ) {
2009-05-08 00:21:17 +08:00
if ( parsingMethod . matches ( token ) )
2009-05-06 06:08:00 +08:00
return parsingMethod . match ( argumentDefinitions , token , position ) ;
2009-05-05 06:41:23 +08:00
}
// No parse results found.
return null ;
}
2009-05-04 08:11:42 +08:00
}
2009-05-06 06:08:00 +08:00
/ * *
* An exception indicating that some required arguments are missing .
* /
2009-05-20 07:26:17 +08:00
class MissingArgumentException extends ArgumentException {
2009-05-06 06:08:00 +08:00
public MissingArgumentException ( Collection < ArgumentDefinition > missingArguments ) {
super ( formatArguments ( missingArguments ) ) ;
}
private static String formatArguments ( Collection < ArgumentDefinition > missingArguments ) {
StringBuilder sb = new StringBuilder ( ) ;
2009-05-08 03:31:32 +08:00
for ( ArgumentDefinition missingArgument : missingArguments ) {
if ( missingArgument . shortName ! = null )
sb . append ( String . format ( "%nArgument with name '--%s' (-%s) is missing." , missingArgument . fullName , missingArgument . shortName ) ) ;
else
sb . append ( String . format ( "%nArgument with name '--%s' is missing." , missingArgument . fullName ) ) ;
}
2009-05-06 06:08:00 +08:00
return sb . toString ( ) ;
}
}
/ * *
* An exception for undefined arguments .
* /
2009-05-20 07:26:17 +08:00
class InvalidArgumentException extends ArgumentException {
2009-08-23 08:56:02 +08:00
public InvalidArgumentException ( ArgumentMatches invalidArguments ) {
2009-05-06 06:08:00 +08:00
super ( formatArguments ( invalidArguments ) ) ;
}
2009-08-23 08:56:02 +08:00
private static String formatArguments ( ArgumentMatches invalidArguments ) {
2009-05-06 06:08:00 +08:00
StringBuilder sb = new StringBuilder ( ) ;
for ( ArgumentMatch invalidArgument : invalidArguments )
2009-05-08 03:31:32 +08:00
sb . append ( String . format ( "%nArgument with name '%s' isn't defined." , invalidArgument . label ) ) ;
2009-05-06 06:08:00 +08:00
return sb . toString ( ) ;
}
}
/ * *
2009-05-08 04:28:56 +08:00
* An exception for values whose format is invalid .
2009-05-06 06:08:00 +08:00
* /
2009-05-20 07:26:17 +08:00
class InvalidArgumentValueException extends ArgumentException {
2009-05-08 04:28:56 +08:00
public InvalidArgumentValueException ( Collection < Pair < ArgumentDefinition , String > > invalidArgumentValues ) {
super ( formatArguments ( invalidArgumentValues ) ) ;
}
private static String formatArguments ( Collection < Pair < ArgumentDefinition , String > > invalidArgumentValues ) {
StringBuilder sb = new StringBuilder ( ) ;
for ( Pair < ArgumentDefinition , String > invalidValue : invalidArgumentValues ) {
2011-07-26 01:20:00 +08:00
if ( invalidValue . getSecond ( ) = = null )
sb . append ( String . format ( "%nArgument '--%s' requires a value but none was provided" ,
invalidValue . first . fullName ) ) ;
else
sb . append ( String . format ( "%nArgument '--%s' has value of incorrect format: %s (should match %s)" ,
invalidValue . first . fullName ,
invalidValue . second ,
invalidValue . first . validation ) ) ;
2009-05-08 04:28:56 +08:00
}
return sb . toString ( ) ;
}
}
/ * *
* An exception for values that can ' t be mated with any argument .
* /
2009-05-20 07:26:17 +08:00
class UnmatchedArgumentException extends ArgumentException {
2009-05-08 04:28:56 +08:00
public UnmatchedArgumentException ( ArgumentMatch invalidValues ) {
2009-05-06 06:08:00 +08:00
super ( formatArguments ( invalidValues ) ) ;
}
private static String formatArguments ( ArgumentMatch invalidValues ) {
StringBuilder sb = new StringBuilder ( ) ;
2011-10-15 00:06:41 +08:00
for ( ArgumentMatchSite site : invalidValues . sites . keySet ( ) )
for ( String value : invalidValues . sites . get ( site ) ) {
switch ( site . getSource ( ) . getType ( ) ) {
case CommandLine :
sb . append ( String . format ( "%nInvalid argument value '%s' at position %d." ,
value , site . getIndex ( ) ) ) ;
break ;
case File :
sb . append ( String . format ( "%nInvalid argument value '%s' in file %s at position %d." ,
value , site . getSource ( ) . getFile ( ) . getAbsolutePath ( ) , site . getIndex ( ) ) ) ;
break ;
default :
throw new RuntimeException ( String . format ( "Unexpected argument match source type: %s" ,
site . getSource ( ) . getType ( ) ) ) ;
}
2011-01-13 07:15:08 +08:00
if ( value ! = null & & Utils . dupString ( ' ' , value . length ( ) ) . equals ( value ) )
sb . append ( " Please make sure any line continuation backslashes on your command line are not followed by whitespace." ) ;
}
2009-05-06 06:08:00 +08:00
return sb . toString ( ) ;
}
}
/ * *
* An exception indicating that too many values have been provided for the given argument .
* /
2009-05-20 07:26:17 +08:00
class TooManyValuesForArgumentException extends ArgumentException {
2009-05-06 06:08:00 +08:00
public TooManyValuesForArgumentException ( Collection < ArgumentMatch > arguments ) {
super ( formatArguments ( arguments ) ) ;
}
private static String formatArguments ( Collection < ArgumentMatch > arguments ) {
StringBuilder sb = new StringBuilder ( ) ;
for ( ArgumentMatch argument : arguments )
2011-01-20 12:38:54 +08:00
sb . append ( String . format ( "%nArgument '%s' has too many values: %s." , argument . label , Arrays . deepToString ( argument . values ( ) . toArray ( ) ) ) ) ;
2009-05-06 06:08:00 +08:00
return sb . toString ( ) ;
}
2009-05-07 21:27:48 +08:00
}
/ * *
* An exception indicating that mutually exclusive options have been passed in the same command line .
* /
2009-05-20 07:26:17 +08:00
class ArgumentsAreMutuallyExclusiveException extends ArgumentException {
2009-05-07 21:27:48 +08:00
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 )
2009-05-08 03:31:32 +08:00
sb . append ( String . format ( "%nArguments '%s' and '%s' are mutually exclusive." , argument . first . definition . fullName , argument . second . definition . fullName ) ) ;
2009-05-07 21:27:48 +08:00
return sb . toString ( ) ;
}
2009-10-10 02:11:32 +08:00
}
/ * *
* An exception for when an argument doesn ' t match an of the enumerated options for that var type
* /
class UnknownEnumeratedValueException extends ArgumentException {
2010-01-21 05:36:46 +08:00
public UnknownEnumeratedValueException ( ArgumentDefinition definition , String argumentPassed ) {
super ( formatArguments ( definition , argumentPassed ) ) ;
2009-10-10 02:11:32 +08:00
}
2010-01-21 05:36:46 +08:00
private static String formatArguments ( ArgumentDefinition definition , String argumentPassed ) {
return String . format ( "Invalid value %s specified for argument %s; valid options are (%s)." , argumentPassed , definition . fullName , Utils . join ( "," , definition . validOptions ) ) ;
2009-10-10 02:11:32 +08:00
}
2011-10-15 00:06:41 +08:00
}
/ * *
* Container class to store the list of argument files .
* The files will be parsed after the command line arguments .
* /
class ParsingEngineArgumentFiles {
@Argument ( fullName = "arg_file" , shortName = "args" , doc = "Reads arguments from the specified file" , required = false )
public List < File > files = new ArrayList < File > ( ) ;
}