Integrate more feedback on command-line argument system. Focus on help
formatter: separate required from optional but otherwise keep ordering the same, reorder GATK arguments by usage. git-svn-id: file:///humgen/gsa-scr1/gsa-engineering/svn_contents/trunk@764 348d0f76-0448-11de-a6fe-93d51630548a
This commit is contained in:
parent
34f9820299
commit
dc748d9c9c
|
|
@ -118,8 +118,8 @@ public class CommandLineGATK extends CommandLineProgram {
|
|||
*/
|
||||
@Override
|
||||
protected Class[] getArgumentSources() {
|
||||
if (analysisName == null)
|
||||
throw new IllegalArgumentException("Must provide analysis name");
|
||||
// No walker info? No plugins.
|
||||
if (analysisName == null) return new Class[] {};
|
||||
|
||||
walkerManager = new WalkerManager(pluginPathName);
|
||||
|
||||
|
|
|
|||
|
|
@ -50,29 +50,28 @@ public class GATKArgumentCollection {
|
|||
public GATKArgumentCollection() {
|
||||
}
|
||||
|
||||
@Element(required=false)
|
||||
public String analysisName = null;
|
||||
|
||||
@ElementMap(entry = "analysis_argument", key = "key", attribute = true, inline = true, required=false)
|
||||
public Map<String, String> walkerArgs = new HashMap<String, String>();
|
||||
|
||||
// parameters and their defaults
|
||||
@ElementList
|
||||
@Argument(fullName = "input_file", shortName = "I", doc = "SAM or BAM file(s)", required = false)
|
||||
public List<File> samFiles = new ArrayList<File>();
|
||||
|
||||
@Element(required=false)
|
||||
@Argument(fullName = "maximum_reads", shortName = "M", doc = "Maximum number of reads to process before exiting", required = false)
|
||||
public String maximumReads = "-1";
|
||||
|
||||
@Element(required=false)
|
||||
@Argument(fullName = "validation_strictness", shortName = "S", doc = "How strict should we be with validation (LENIENT|SILENT|STRICT)", required = false)
|
||||
public String strictnessLevel = "strict";
|
||||
@Argument(fullName = "intervals", shortName = "L", doc = "A list of genomic intervals over which to operate. Can be explicitly specified on the command line or in a file.", required = false)
|
||||
public String intervals = null;
|
||||
|
||||
@Element(required=false)
|
||||
@Argument(fullName = "reference_sequence", shortName = "R", doc = "Reference sequence file", required = false)
|
||||
public File referenceFile = null;
|
||||
|
||||
@Element(required=false)
|
||||
public String analysisName = null;
|
||||
|
||||
// parameters and their defaults
|
||||
@ElementMap(entry = "analysis_argument", key = "key", attribute = true, inline = true, required=false)
|
||||
public Map<String, String> walkerArgs = new HashMap<String, String>();
|
||||
@ElementList(required=false)
|
||||
@Argument(fullName = "rodBind", shortName = "B", doc = "Bindings for reference-ordered data, in the form <name>,<type>,<file>", required = false)
|
||||
public ArrayList<String> RODBindings = new ArrayList<String>();
|
||||
|
||||
@Element(required=false)
|
||||
@Argument(fullName = "DBSNP", shortName = "D", doc = "DBSNP file", required = false)
|
||||
|
|
@ -86,38 +85,6 @@ public class GATKArgumentCollection {
|
|||
@Argument(fullName = "hapmap_chip", shortName = "hc", doc = "Hapmap chip file", required = false)
|
||||
public String HAPMAPChipFile = null;
|
||||
|
||||
@Element(required=false)
|
||||
@Argument(fullName = "threaded_IO", shortName = "P", doc = "If set, enables threaded I/O operations", required = false)
|
||||
public Boolean enabledThreadedIO = false;
|
||||
|
||||
@Element(required=false)
|
||||
@Argument(fullName = "unsafe", shortName = "U", doc = "If set, enables unsafe operations, nothing will be checked at runtime.", required = false)
|
||||
public Boolean unsafe = false;
|
||||
|
||||
@Element(required=false)
|
||||
@Argument(fullName = "sort_on_the_fly", shortName = "sort", doc = "Maximum number of reads to sort on the fly", required = false)
|
||||
public Integer maximumReadSorts = null;
|
||||
|
||||
@Element(required=false)
|
||||
@Argument(fullName = "downsample_to_fraction", shortName = "dfrac", doc = "Fraction [0.0-1.0] of reads to downsample to", required = false)
|
||||
public Double downsampleFraction = null;
|
||||
|
||||
@Element(required=false)
|
||||
@Argument(fullName = "downsample_to_coverage", shortName = "dcov", doc = "Coverage [integer] to downsample to", required = false)
|
||||
public Integer downsampleCoverage = null;
|
||||
|
||||
@Element(required=false)
|
||||
@Argument(fullName = "intervals", shortName = "L", doc = "A list of genomic intervals over which to operate. Can be explicitly specified on the command line or in a file.", required = false)
|
||||
public String intervals = null;
|
||||
|
||||
@Element(required=false)
|
||||
@Argument(fullName = "all_loci", shortName = "A", doc = "Should we process all loci, not just those covered by reads", required = false)
|
||||
public Boolean walkAllLoci = false;
|
||||
|
||||
@Element(required=false)
|
||||
@Argument(fullName = "disablethreading", shortName = "dt", doc = "Disable experimental threading support.", required = false)
|
||||
public Boolean disableThreading = false;
|
||||
|
||||
/** An output file presented to the walker. */
|
||||
@Element(required=false)
|
||||
@Argument(fullName = "out", shortName = "o", doc = "An output file presented to the walker. Will overwrite contents if file exists.", required = false)
|
||||
|
|
@ -133,15 +100,47 @@ public class GATKArgumentCollection {
|
|||
@Argument(fullName = "outerr", shortName = "oe", doc = "A joint file for 'normal' and error output presented to the walker. Will overwrite contents if file exists.", required = false)
|
||||
public String outErrFileName = null;
|
||||
|
||||
@Element(required=false)
|
||||
@Argument(fullName = "all_loci", shortName = "A", doc = "Should we process all loci, not just those covered by reads", required = false)
|
||||
public Boolean walkAllLoci = false;
|
||||
|
||||
@Element(required=false)
|
||||
@Argument(fullName = "maximum_reads", shortName = "M", doc = "Maximum number of reads to process before exiting", required = false)
|
||||
public String maximumReads = "-1";
|
||||
|
||||
@Element(required=false)
|
||||
@Argument(fullName = "sort_on_the_fly", shortName = "sort", doc = "Maximum number of reads to sort on the fly", required = false)
|
||||
public Integer maximumReadSorts = null;
|
||||
|
||||
@Element(required=false)
|
||||
@Argument(fullName = "downsample_to_fraction", shortName = "dfrac", doc = "Fraction [0.0-1.0] of reads to downsample to", required = false)
|
||||
public Double downsampleFraction = null;
|
||||
|
||||
@Element(required=false)
|
||||
@Argument(fullName = "downsample_to_coverage", shortName = "dcov", doc = "Coverage [integer] to downsample to", required = false)
|
||||
public Integer downsampleCoverage = null;
|
||||
|
||||
@Element(required=false)
|
||||
@Argument(fullName = "validation_strictness", shortName = "S", doc = "How strict should we be with validation (LENIENT|SILENT|STRICT)", required = false)
|
||||
public String strictnessLevel = "strict";
|
||||
|
||||
@Element(required=false)
|
||||
@Argument(fullName = "unsafe", shortName = "U", doc = "If set, enables unsafe operations, nothing will be checked at runtime.", required = false)
|
||||
public Boolean unsafe = false;
|
||||
|
||||
@Element(required=false)
|
||||
@Argument(fullName = "threaded_IO", shortName = "P", doc = "If set, enables threaded I/O operations", required = false)
|
||||
public Boolean enabledThreadedIO = false;
|
||||
|
||||
@Element(required=false)
|
||||
@Argument(fullName = "disablethreading", shortName = "dt", doc = "Disable experimental threading support.", required = false)
|
||||
public Boolean disableThreading = false;
|
||||
|
||||
/** How many threads should be allocated to this analysis. */
|
||||
@Element(required=false)
|
||||
@Argument(fullName = "numthreads", shortName = "nt", doc = "How many threads should be allocated to running this analysis.", required = false)
|
||||
public int numberOfThreads = 1;
|
||||
|
||||
@ElementList(required=false)
|
||||
@Argument(fullName = "rodBind", shortName = "B", doc = "", required = false)
|
||||
public ArrayList<String> RODBindings = new ArrayList<String>();
|
||||
|
||||
/**
|
||||
* marshal the data out to a object
|
||||
*
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ class ArgumentDefinitions implements Iterable<ArgumentDefinition> {
|
|||
while( definitionGroupIterator.hasNext() ) {
|
||||
ArgumentDefinitionGroup candidate = definitionGroupIterator.next();
|
||||
if( candidate.groupNameMatches(argumentDefinitionGroup) ) {
|
||||
argumentDefinitionGroup = argumentDefinitionGroup.mergeInto(candidate);
|
||||
argumentDefinitionGroup = candidate.merge(argumentDefinitionGroup);
|
||||
definitionGroupIterator.remove();
|
||||
}
|
||||
}
|
||||
|
|
@ -184,11 +184,11 @@ class ArgumentDefinitionGroup implements Iterable<ArgumentDefinition> {
|
|||
/**
|
||||
* The argument definitions associated with this group.
|
||||
*/
|
||||
public final Collection<ArgumentDefinition> argumentDefinitions;
|
||||
public final List<ArgumentDefinition> argumentDefinitions;
|
||||
|
||||
public ArgumentDefinitionGroup( String groupName, Collection<ArgumentDefinition> argumentDefinitions ) {
|
||||
public ArgumentDefinitionGroup( String groupName, List<ArgumentDefinition> argumentDefinitions ) {
|
||||
this.groupName = groupName;
|
||||
this.argumentDefinitions = Collections.unmodifiableCollection( argumentDefinitions );
|
||||
this.argumentDefinitions = Collections.unmodifiableList( argumentDefinitions );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -207,12 +207,12 @@ class ArgumentDefinitionGroup implements Iterable<ArgumentDefinition> {
|
|||
* group since argument groups are supposed to be immutable. Asserts that
|
||||
* both argument groups have the same name.
|
||||
*/
|
||||
public ArgumentDefinitionGroup mergeInto( ArgumentDefinitionGroup other ) {
|
||||
public ArgumentDefinitionGroup merge( ArgumentDefinitionGroup other ) {
|
||||
if( !groupNameMatches(other) )
|
||||
throw new StingException("Unable to merge two argument groups with differing names.");
|
||||
|
||||
// Create a merged definition group.
|
||||
Collection<ArgumentDefinition> mergedDefinitions = new ArrayList<ArgumentDefinition>();
|
||||
List<ArgumentDefinition> mergedDefinitions = new ArrayList<ArgumentDefinition>();
|
||||
mergedDefinitions.addAll(this.argumentDefinitions);
|
||||
mergedDefinitions.addAll(other.argumentDefinitions);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,12 @@
|
|||
package org.broadinstitute.sting.utils.cmdLine;
|
||||
|
||||
import org.broadinstitute.sting.utils.StingException;
|
||||
|
||||
import java.util.Formatter;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.Comparator;
|
||||
import java.util.TreeSet;
|
||||
import java.util.SortedSet;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.Matcher;
|
||||
/**
|
||||
|
|
@ -43,80 +38,36 @@ public class HelpFormatter {
|
|||
* @param argumentDefinitions Argument definitions for which help should be printed.
|
||||
*/
|
||||
public void printHelp( String runningInstructions, ArgumentDefinitions argumentDefinitions ) {
|
||||
SortedSet<ArgumentDefinition> mainArguments = getMainArguments( argumentDefinitions );
|
||||
Map<String,SortedSet<ArgumentDefinition>> pluginsByGroup = getPluginArguments( argumentDefinitions );
|
||||
|
||||
System.out.printf("%s%s%n",
|
||||
getSynopsis(runningInstructions,mainArguments,pluginsByGroup),
|
||||
getDetailed(mainArguments,pluginsByGroup) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a sorted set of the command-line arguments for the main application from the ArgumentDefinitions object.
|
||||
* @param argumentDefinitions Predefined argument definitions.
|
||||
* @return A list of argument definitions.
|
||||
*/
|
||||
private SortedSet<ArgumentDefinition> getMainArguments( ArgumentDefinitions argumentDefinitions ) {
|
||||
for( ArgumentDefinitionGroup argumentGroup: argumentDefinitions.getArgumentDefinitionGroups() ) {
|
||||
// A null groupName is the signal that these are the application's main arguments.
|
||||
if( argumentGroup.groupName == null ){
|
||||
SortedSet<ArgumentDefinition> sortedDefinitions = new TreeSet<ArgumentDefinition>( new ArgumentDefinitionComparator() );
|
||||
sortedDefinitions.addAll( argumentGroup.argumentDefinitions );
|
||||
return sortedDefinitions;
|
||||
}
|
||||
}
|
||||
throw new StingException( "Unable to retrieve main command-line arguments." );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a sorted set of the command-line arguments for application plugin objects.
|
||||
* @param argumentDefinitions Predefined argument definitions.
|
||||
* @return A map of argument group name -> a list of argument definitions.
|
||||
*/
|
||||
private Map<String,SortedSet<ArgumentDefinition>> getPluginArguments( ArgumentDefinitions argumentDefinitions ) {
|
||||
Map<String,SortedSet<ArgumentDefinition>> pluginsByGroup = new HashMap<String,SortedSet<ArgumentDefinition>>();
|
||||
for( ArgumentDefinitionGroup argumentGroup: argumentDefinitions.getArgumentDefinitionGroups() ) {
|
||||
// A null groupName is the signal that these are the application's main arguments.
|
||||
if( argumentGroup.groupName == null )
|
||||
continue;
|
||||
SortedSet<ArgumentDefinition> sortedDefinitions = new TreeSet<ArgumentDefinition>( new ArgumentDefinitionComparator() );
|
||||
sortedDefinitions.addAll( argumentGroup.argumentDefinitions );
|
||||
pluginsByGroup.put( argumentGroup.groupName, sortedDefinitions );
|
||||
}
|
||||
return pluginsByGroup;
|
||||
List<ArgumentDefinitionGroup> argumentGroups = prepareArgumentGroups( argumentDefinitions );
|
||||
System.out.printf("%s%s%n",getSynopsis(runningInstructions,argumentGroups),getDetailed(argumentGroups) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the synopsis: the actual command to run.
|
||||
* @param mainArguments Main program arguments.
|
||||
* @param runningInstructions Instructions on how to run hte application.
|
||||
* @param pluginArgumentGroups Groups of plugin arguments
|
||||
* @param argumentGroups Program arguments sorted in order of definition group displays.
|
||||
* @return A synopsis line.
|
||||
*/
|
||||
private String getSynopsis( String runningInstructions,
|
||||
SortedSet<ArgumentDefinition> mainArguments,
|
||||
Map<String,SortedSet<ArgumentDefinition>> pluginArgumentGroups ) {
|
||||
List<ArgumentDefinitionGroup> argumentGroups ) {
|
||||
// Build out the synopsis all as one long line.
|
||||
StringBuilder lineBuilder = new StringBuilder();
|
||||
Formatter lineFormatter = new Formatter( lineBuilder );
|
||||
|
||||
lineFormatter.format("java %s", runningInstructions);
|
||||
|
||||
List<ArgumentDefinition> argumentDefinitions = new ArrayList<ArgumentDefinition>();
|
||||
argumentDefinitions.addAll( mainArguments );
|
||||
for( SortedSet pluginArguments: pluginArgumentGroups.values() )
|
||||
argumentDefinitions.addAll( pluginArguments );
|
||||
|
||||
for( ArgumentDefinition argumentDefinition: argumentDefinitions ) {
|
||||
lineFormatter.format(" ");
|
||||
if( !argumentDefinition.required ) lineFormatter.format("[");
|
||||
if( argumentDefinition.shortName != null )
|
||||
lineFormatter.format("-%s", argumentDefinition.shortName);
|
||||
else
|
||||
lineFormatter.format("--%s", argumentDefinition.fullName);
|
||||
if( !argumentDefinition.isFlag() )
|
||||
lineFormatter.format(" <%s>", argumentDefinition.fullName);
|
||||
if( !argumentDefinition.required ) lineFormatter.format("]");
|
||||
for( ArgumentDefinitionGroup argumentGroup: argumentGroups ) {
|
||||
for( ArgumentDefinition argumentDefinition: argumentGroup.argumentDefinitions ) {
|
||||
lineFormatter.format(" ");
|
||||
if( !argumentDefinition.required ) lineFormatter.format("[");
|
||||
if( argumentDefinition.shortName != null )
|
||||
lineFormatter.format("-%s", argumentDefinition.shortName);
|
||||
else
|
||||
lineFormatter.format("--%s", argumentDefinition.fullName);
|
||||
if( !argumentDefinition.isFlag() )
|
||||
lineFormatter.format(" <%s>", argumentDefinition.fullName);
|
||||
if( !argumentDefinition.required ) lineFormatter.format("]");
|
||||
}
|
||||
}
|
||||
|
||||
// Word wrap the synopsis.
|
||||
|
|
@ -137,23 +88,27 @@ public class HelpFormatter {
|
|||
|
||||
/**
|
||||
* Gets detailed output about each argument type.
|
||||
* @param mainArguments Main program arguments.
|
||||
* @param pluginArgumentGroups Groups of plugin arguments
|
||||
* @param argumentGroups Collection of program arguments sorted according to how they should be shown.
|
||||
* @return Detailed text about all arguments.
|
||||
*/
|
||||
private String getDetailed( SortedSet<ArgumentDefinition> mainArguments, Map<String,SortedSet<ArgumentDefinition>> pluginArgumentGroups ) {
|
||||
private String getDetailed( List<ArgumentDefinitionGroup> argumentGroups ) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.append( getDetailForGroup( mainArguments ) );
|
||||
|
||||
for( String groupName: pluginArgumentGroups.keySet() ) {
|
||||
builder.append( String.format("Arguments for %s:%n", groupName ) );
|
||||
builder.append( getDetailForGroup( pluginArgumentGroups.get(groupName) ) );
|
||||
for( ArgumentDefinitionGroup argumentGroup: argumentGroups ) {
|
||||
if( argumentGroup.groupName != null && argumentGroup.argumentDefinitions.size() != 0 )
|
||||
builder.append( String.format("%nArguments for %s:%n", argumentGroup.groupName ) );
|
||||
builder.append( getDetailForGroup( argumentGroup.argumentDefinitions ) );
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private String getDetailForGroup( SortedSet<ArgumentDefinition> argumentDefinitions ) {
|
||||
/**
|
||||
* Gets a detailed description for a given argument group.
|
||||
* @param argumentDefinitions The argument definitions contained withina group.
|
||||
* @return A string giving detailed info about the contents of this group.
|
||||
*/
|
||||
private String getDetailForGroup( List<ArgumentDefinition> argumentDefinitions ) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
Formatter formatter = new Formatter( builder );
|
||||
|
||||
|
|
@ -237,21 +192,43 @@ public class HelpFormatter {
|
|||
}
|
||||
|
||||
/**
|
||||
* A comparator to reorder arguments in alphabetical order.
|
||||
* Extract the argument definition groups from the argument definitions and arrange them appropriately.
|
||||
* For help, we want the arguments sorted as they are declared in the class. However, required arguments
|
||||
* should appear before optional arguments.
|
||||
* @param argumentDefinitions Argument definitions from which to extract argument groups.
|
||||
* @return A list of argument groups sorted in display order.
|
||||
*/
|
||||
private class ArgumentDefinitionComparator implements Comparator<ArgumentDefinition> {
|
||||
public int compare( ArgumentDefinition lhs, ArgumentDefinition rhs ) {
|
||||
if( lhs.shortName == null && rhs.shortName == null )
|
||||
return lhs.fullName.compareTo( rhs.fullName );
|
||||
// short names always come before long names.
|
||||
else if ( lhs.shortName == null )
|
||||
return -1;
|
||||
else if ( rhs.shortName == null )
|
||||
return 1;
|
||||
else if ( lhs.shortName.equals(rhs.shortName) )
|
||||
return lhs.fullName.compareTo( rhs.fullName );
|
||||
else
|
||||
return lhs.shortName.compareTo( rhs.shortName );
|
||||
private List<ArgumentDefinitionGroup> prepareArgumentGroups( ArgumentDefinitions argumentDefinitions ) {
|
||||
// Sort the list of argument definitions according to how they should be shown.
|
||||
// Put the sorted results into a new cloned data structure.
|
||||
Comparator definitionComparator = new Comparator<ArgumentDefinition>() {
|
||||
public int compare( ArgumentDefinition lhs, ArgumentDefinition rhs ) {
|
||||
if( lhs.required && rhs.required ) return 0;
|
||||
if( lhs.required ) return -1;
|
||||
if( rhs.required ) return 1;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
List<ArgumentDefinitionGroup> argumentGroups = new ArrayList();
|
||||
for( ArgumentDefinitionGroup argumentGroup: argumentDefinitions.getArgumentDefinitionGroups() ) {
|
||||
List<ArgumentDefinition> sortedDefinitions = new ArrayList( argumentGroup.argumentDefinitions );
|
||||
Collections.sort( sortedDefinitions, definitionComparator );
|
||||
argumentGroups.add( new ArgumentDefinitionGroup(argumentGroup.groupName,sortedDefinitions) );
|
||||
}
|
||||
|
||||
// Sort the argument groups themselves with main arguments first, followed by plugins sorted in name order.
|
||||
Comparator groupComparator = new Comparator<ArgumentDefinitionGroup>() {
|
||||
public int compare( ArgumentDefinitionGroup lhs, ArgumentDefinitionGroup rhs ) {
|
||||
if( lhs.groupName == null && rhs.groupName == null ) return 0;
|
||||
if( lhs.groupName == null ) return -1;
|
||||
if( rhs.groupName == null ) return 1;
|
||||
return lhs.groupName.compareTo(rhs.groupName);
|
||||
}
|
||||
};
|
||||
Collections.sort( argumentGroups, groupComparator );
|
||||
|
||||
|
||||
return argumentGroups;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ public class ParsingEngine {
|
|||
* @param source An argument source from which to extract command-line arguments.
|
||||
*/
|
||||
public void addArgumentSource( String sourceName, Class source ) {
|
||||
Collection<ArgumentDefinition> argumentsFromSource = new ArrayList<ArgumentDefinition>();
|
||||
List<ArgumentDefinition> argumentsFromSource = new ArrayList<ArgumentDefinition>();
|
||||
while( source != null ) {
|
||||
Field[] fields = source.getDeclaredFields();
|
||||
for( Field field: fields ) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue