diff --git a/java/src/org/broadinstitute/sting/gatk/CommandLineExecutable.java b/java/src/org/broadinstitute/sting/gatk/CommandLineExecutable.java index 195043379..38f7a6131 100644 --- a/java/src/org/broadinstitute/sting/gatk/CommandLineExecutable.java +++ b/java/src/org/broadinstitute/sting/gatk/CommandLineExecutable.java @@ -13,7 +13,6 @@ import java.util.ArrayList; import net.sf.samtools.SAMFileReader; - /* * Copyright (c) 2009 The Broad Institute * diff --git a/java/src/org/broadinstitute/sting/gatk/CommandLineGATK.java b/java/src/org/broadinstitute/sting/gatk/CommandLineGATK.java index c61461fc2..806b922ed 100755 --- a/java/src/org/broadinstitute/sting/gatk/CommandLineGATK.java +++ b/java/src/org/broadinstitute/sting/gatk/CommandLineGATK.java @@ -2,8 +2,7 @@ package org.broadinstitute.sting.gatk; import org.broadinstitute.sting.utils.cmdLine.*; -import java.util.List; -import java.util.ArrayList; +import java.util.*; /** * @@ -34,9 +33,6 @@ import java.util.ArrayList; * the gatk engine should deal with any data related information. */ public class CommandLineGATK extends CommandLineExecutable { - // our genome analysis engine - private GenomeAnalysisEngine GATKEngine = null; - @Argument(fullName = "analysis_type", shortName = "T", doc = "Type of analysis to run") private String analysisName = null; @@ -49,14 +45,10 @@ public class CommandLineGATK extends CommandLineExecutable { * @return A list of Strings that contain pleasant info about the GATK. */ @Override - protected List getApplicationHeader() { - List header = new ArrayList(); - header.add("The Genome Analysis Toolkit (GATK)"); - header.add("Copyright (c) 2009 The Broad Institute"); - header.add("Please view our documentation at http://www.broadinstitute.org/gsa/wiki"); - header.add("For support, email gsadevelopers@broadinstitute.org"); - header.add(""); - return header; + protected ApplicationDetails getApplicationDetails() { + return new ApplicationDetails( createApplicationHeader(), + ApplicationDetails.createDefaultRunningInstructions(getClass()), + getAdditionalHelp() ); } @Override @@ -78,4 +70,60 @@ public class CommandLineGATK extends CommandLineExecutable { exitSystemWithError(e); } } + + /** + * Creates the a short blurb about the GATK, copyright info, and where to get documentation. + * @return The application header. + */ + private List createApplicationHeader() { + List header = new ArrayList(); + header.add("The Genome Analysis Toolkit (GATK)"); + header.add("Copyright (c) 2009 The Broad Institute"); + header.add("Please view our documentation at http://www.broadinstitute.org/gsa/wiki"); + header.add("For support, email gsadevelopers@broadinstitute.org"); + header.add(""); + return header; + } + + /** + * Retrieves additional information about GATK walkers. + * TODO: This functionality is very similar to that employed by the HelpFormatter. Generalize + * the code in HelpFormatter and supply it as a helper to this method. + * @return A string summarizing the walkers available in this distribution. + */ + private String getAdditionalHelp() { + // Get the list of walker names from the walker manager. + Set walkerNames = GATKEngine.getWalkerNames(); + + // Sort the list of walker names. + walkerNames = new TreeSet( walkerNames ); + + // Construct a help string to output available walkers. + StringBuilder additionalHelp = new StringBuilder(); + Formatter formatter = new Formatter( additionalHelp ); + + formatter.format( "Available analyses:%n" ); + + // Compute the max size of any walker name + int maxNameLength = 0; + for( String walkerName: walkerNames ) { + if( maxNameLength < walkerName.length() ) + maxNameLength = walkerName.length(); + } + + final int fieldWidth = maxNameLength + HelpFormatter.FIELD_SEPARATION_WIDTH; + final int walkersPerLine = Math.min(HelpFormatter.LINE_WIDTH / fieldWidth, 4 ); + final int columnSpacing = (HelpFormatter.LINE_WIDTH - (fieldWidth * walkersPerLine)) / walkersPerLine; + + int currentWalkerName = 0; + for( String walkerName: walkerNames ) { + formatter.format( "%-" + HelpFormatter.FIELD_SEPARATION_WIDTH + "s" + + "%-" + fieldWidth + "s" + + "%-" + columnSpacing + "s", "", walkerName, "" ); + if( ++currentWalkerName % walkersPerLine == 0 ) + formatter.format("%n"); + } + + return additionalHelp.toString(); + } } diff --git a/java/src/org/broadinstitute/sting/gatk/GenomeAnalysisEngine.java b/java/src/org/broadinstitute/sting/gatk/GenomeAnalysisEngine.java index 8836d99f4..0a04e0f60 100755 --- a/java/src/org/broadinstitute/sting/gatk/GenomeAnalysisEngine.java +++ b/java/src/org/broadinstitute/sting/gatk/GenomeAnalysisEngine.java @@ -41,6 +41,7 @@ import org.broadinstitute.sting.utils.cmdLine.ArgumentException; import java.io.File; import java.util.ArrayList; import java.util.List; +import java.util.Set; public class GenomeAnalysisEngine { @@ -144,20 +145,21 @@ public class GenomeAnalysisEngine { return microScheduler.execute(my_walker, locs, argCollection.maximumEngineIterations); } + /** + * Gets a set of the names of all walkers that the GATK has discovered. + * @return A set of the names of all discovered walkers. + */ + public Set getWalkerNames() { + return walkerManager.getWalkerNames(); + } + /** * Retrieves an instance of the walker based on the walker name. * @param walkerName Name of the walker. Must not be null. If the walker cannot be instantiated, an exception will be thrown. * @return An instance of the walker. */ public Walker getWalkerByName( String walkerName ) { - try { - return walkerManager.createWalkerByName(walkerName); - } catch (InstantiationException ex) { - throw new StingException("Unable to instantiate walker.", ex); - } - catch (IllegalAccessException ex) { - throw new StingException("Unable to access walker", ex); - } + return walkerManager.createWalkerByName(walkerName); } diff --git a/java/src/org/broadinstitute/sting/gatk/WalkerManager.java b/java/src/org/broadinstitute/sting/gatk/WalkerManager.java index 5d211199b..3408e7ad3 100755 --- a/java/src/org/broadinstitute/sting/gatk/WalkerManager.java +++ b/java/src/org/broadinstitute/sting/gatk/WalkerManager.java @@ -1,3 +1,28 @@ +/* + * Copyright (c) 2009 The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * 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: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * 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.gatk; import java.util.*; @@ -47,12 +72,27 @@ public class WalkerManager { * @param walkerName Name of the walker to retrieve. * @return The walker object if found; null otherwise. */ - public Walker createWalkerByName(String walkerName) - throws InstantiationException, IllegalAccessException { - Class walker = walkersByName.get(walkerName); - if( walker == null ) - throw new StingException(String.format("Could not find walker with name: %s", walkerName)); - return walker.newInstance(); + public Walker createWalkerByName(String walkerName) { + try { + Class walker = walkersByName.get(walkerName); + if( walker == null ) + throw new StingException(String.format("Could not find walker with name: %s", walkerName)); + return walker.newInstance(); + } + catch( InstantiationException ex ) { + throw new StingException("Unable to instantiate walker " + walkerName, ex); + } + catch( IllegalAccessException ex ) { + throw new StingException("Unable to access walker " + walkerName, ex); + } + } + + /** + * Get the list of walkers currently available to the GATK. + * @return Names of currently available walkers. + */ + public Set getWalkerNames() { + return Collections.unmodifiableSet( walkersByName.keySet() ); } /** @@ -177,7 +217,6 @@ public class WalkerManager { for (Class walkerClass : walkerClasses) { String walkerName = getWalkerName(walkerClass); - logger.info(String.format("* Adding module %s", walkerName)); walkers.put(walkerName, walkerClass); } diff --git a/java/src/org/broadinstitute/sting/utils/JVMUtils.java b/java/src/org/broadinstitute/sting/utils/JVMUtils.java index c4d63b08e..8746693e9 100755 --- a/java/src/org/broadinstitute/sting/utils/JVMUtils.java +++ b/java/src/org/broadinstitute/sting/utils/JVMUtils.java @@ -1,6 +1,8 @@ package org.broadinstitute.sting.utils; import java.lang.reflect.Modifier; +import java.io.File; +import java.io.IOException; /** * Created by IntelliJ IDEA. @@ -17,6 +19,25 @@ public class JVMUtils { */ private JVMUtils() { } + /** + * Determines which location contains the specified class. + * + * @return Location (either jar file or directory) of path containing class. + */ + public static File getLocationFor( Class clazz ) throws IOException { + try { + java.net.URI locationURI = clazz.getProtectionDomain().getCodeSource().getLocation().toURI(); + return new File(locationURI); + } + catch (java.net.URISyntaxException ex) { + // a URISyntaxException here must be an IO error; wrap as such. + throw new IOException(ex); + } + catch ( NullPointerException ne ) { + throw new IOException("Can not extract code source location for "+clazz.getName()); + } + } + /** * Is the specified class a concrete implementation of baseClass? * @param clazz Class to check. diff --git a/java/src/org/broadinstitute/sting/utils/cmdLine/ApplicationDetails.java b/java/src/org/broadinstitute/sting/utils/cmdLine/ApplicationDetails.java new file mode 100644 index 000000000..c3f5fcb71 --- /dev/null +++ b/java/src/org/broadinstitute/sting/utils/cmdLine/ApplicationDetails.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2009 The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * 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: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * 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.utils.cmdLine; + +import org.broadinstitute.sting.utils.JVMUtils; +import org.broadinstitute.sting.utils.StingException; + +import java.util.List; +import java.util.Collections; +import java.io.IOException; + +/** + * Contains details additional details that the program can + * supply about itself. + * + * @author hanna + * @version 0.1 + */ + +public class ApplicationDetails { + /** + * Retrieve key information about the application (name, who to contact for support, etc.). + */ + final List applicationHeader; + + /** + * Extract details covering exactly how to run this executable. + */ + final String runningInstructions; + + /** + * Additional help particular to this command-line application. + */ + final String additionalHelp; + + public ApplicationDetails( List applicationHeader, String runningInstructions ) { + this(applicationHeader,runningInstructions,null); + } + + public ApplicationDetails( List applicationHeader, String runningInstructions, String additionalHelp ) { + this.applicationHeader = applicationHeader; + this.runningInstructions = runningInstructions; + this.additionalHelp = additionalHelp; + } + + public static List createDefaultHeader(Class application) { + return Collections.singletonList("Program Name: " + application.getName()); + } + + public static String createDefaultRunningInstructions(Class application) { + // Default implementation to find a command line that makes sense. + // If the user is running from a jar, return '-jar '; otherwise + // return the full class name. + String runningInstructions = null; + try { + runningInstructions = JVMUtils.getLocationFor( application ).getName(); + } + catch( IOException ex ) { + throw new StingException("Unable to determine running instructions", ex); + } + + if( runningInstructions.endsWith(".jar") ) + runningInstructions = String.format("-jar %s", runningInstructions); + else + runningInstructions = application.getName(); + + return runningInstructions; + } +} diff --git a/java/src/org/broadinstitute/sting/utils/cmdLine/CommandLineProgram.java b/java/src/org/broadinstitute/sting/utils/cmdLine/CommandLineProgram.java index b9a00b590..6973dbd1f 100644 --- a/java/src/org/broadinstitute/sting/utils/cmdLine/CommandLineProgram.java +++ b/java/src/org/broadinstitute/sting/utils/cmdLine/CommandLineProgram.java @@ -1,16 +1,12 @@ package org.broadinstitute.sting.utils.cmdLine; import org.apache.log4j.*; -import org.broadinstitute.sting.utils.JVMUtils; -import org.broadinstitute.sting.utils.StingException; import java.io.IOException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.EnumSet; import java.util.Enumeration; -import java.util.Collections; -import java.util.List; /** * User: aaron @@ -91,30 +87,12 @@ public abstract class CommandLineProgram { private static String debugPatternString = "%n[level] %p%n[date]\t\t %d{dd MMM yyyy HH:mm:ss,SSS} %n[class]\t\t %C %n[location]\t %l %n[line number]\t %L %n[message]\t %m %n"; /** - * Retrieves text from the application regarding what parameters to put on the - * JVM command line to run this application. - * @return helpful instructions on the class / jar to run. + * Allows a given application to return a brief description of itself. + * @return An ApplicationDetails object describing the current application. Should not be null. */ - protected String getRunningInstructions() { - // Default implementation to find a command line that makes sense. - // If the user is running from a jar, return '-jar '; otherwise - // return the full class name. - String runningInstructions = getClass().getName(); - - if( runningInstructions.endsWith(".jar") ) - runningInstructions = String.format("-jar %s", runningInstructions); - else - runningInstructions = getClass().getName(); - - return runningInstructions; - } - - /** - * Allows a given application to return a few lines of text describing the application. - * @return A few lines of text describing the application. Should not be null. - */ - protected List getApplicationHeader() { - return Collections.singletonList("Program Name: " + getClass().getName()); + protected ApplicationDetails getApplicationDetails() { + return new ApplicationDetails( ApplicationDetails.createDefaultHeader(getClass()), + ApplicationDetails.createDefaultRunningInstructions(getClass()) ); } /** @@ -260,7 +238,7 @@ public abstract class CommandLineProgram { System.exit(result); } catch (ArgumentException e) { - clp.parser.printHelp( clp.getRunningInstructions() ); + clp.parser.printHelp( clp.getApplicationDetails() ); // Rethrow the exception to exit with an error. throw e; } @@ -307,7 +285,7 @@ public abstract class CommandLineProgram { java.util.Date date = new java.util.Date(); logger.info("-------------------------------------------------------"); - for( String headerLine: clp.getApplicationHeader() ) + for( String headerLine: clp.getApplicationDetails().applicationHeader ) logger.info(headerLine); String output = ""; for (String str : args) { @@ -378,7 +356,7 @@ public abstract class CommandLineProgram { * @param parser True if help is present; false otherwise. */ private static void printHelpAndExit( CommandLineProgram clp, ParsingEngine parser ) { - parser.printHelp( clp.getRunningInstructions() ); + parser.printHelp( clp.getApplicationDetails() ); System.exit(0); } diff --git a/java/src/org/broadinstitute/sting/utils/cmdLine/HelpFormatter.java b/java/src/org/broadinstitute/sting/utils/cmdLine/HelpFormatter.java index 964d31f6a..7b9cb51f5 100755 --- a/java/src/org/broadinstitute/sting/utils/cmdLine/HelpFormatter.java +++ b/java/src/org/broadinstitute/sting/utils/cmdLine/HelpFormatter.java @@ -30,16 +30,21 @@ public class HelpFormatter { /** * Target this line width. */ - private static final int LINE_WIDTH = 100; - private static final int ARG_DOC_SEPARATION_WIDTH = 3; + public static final int LINE_WIDTH = 100; + public static final int FIELD_SEPARATION_WIDTH = 3; /** * Prints the help, given a collection of argument definitions. * @param argumentDefinitions Argument definitions for which help should be printed. */ - public void printHelp( String runningInstructions, ArgumentDefinitions argumentDefinitions ) { + public void printHelp( ApplicationDetails applicationDetails, ArgumentDefinitions argumentDefinitions ) { List argumentGroups = prepareArgumentGroups( argumentDefinitions ); - System.out.printf("%s%s%n",getSynopsis(runningInstructions,argumentGroups),getDetailed(argumentGroups) ); + + String synopsis = getSynopsis(applicationDetails.runningInstructions,argumentGroups); + String additionalDetails = applicationDetails.additionalHelp != null ? applicationDetails.additionalHelp : ""; + String detailedDescription = getDetailed(argumentGroups); + + System.out.printf("%s%n%s%n%s%n",synopsis,detailedDescription,additionalDetails ); } /** @@ -114,8 +119,8 @@ public class HelpFormatter { // Try to fit the entire argument definition across the screen, but impose an arbitrary cap of 3/4 * // LINE_WIDTH in case the length of the arguments gets out of control. - int argWidth = Math.min( findLongestArgumentCallingInfo(argumentDefinitions), (LINE_WIDTH*3)/4 - ARG_DOC_SEPARATION_WIDTH ); - int docWidth = LINE_WIDTH - argWidth - ARG_DOC_SEPARATION_WIDTH; + int argWidth = Math.min( findLongestArgumentCallingInfo(argumentDefinitions), (LINE_WIDTH*3)/4 - FIELD_SEPARATION_WIDTH ); + int docWidth = LINE_WIDTH - argWidth - FIELD_SEPARATION_WIDTH; for( ArgumentDefinition argumentDefinition: argumentDefinitions ) { Iterator wordWrappedArgs = wordWrap( getArgumentCallingInfo(argumentDefinition), argWidth ).iterator(); @@ -125,7 +130,7 @@ public class HelpFormatter { String arg = wordWrappedArgs.hasNext() ? wordWrappedArgs.next() : ""; String doc = wordWrappedDoc.hasNext() ? wordWrappedDoc.next() : ""; - String formatString = "%-" + argWidth + "s%" + ARG_DOC_SEPARATION_WIDTH + "s%s%n"; + String formatString = "%-" + argWidth + "s%" + FIELD_SEPARATION_WIDTH + "s%s%n"; formatter.format( formatString, arg, "", doc ); } } diff --git a/java/src/org/broadinstitute/sting/utils/cmdLine/ParsingEngine.java b/java/src/org/broadinstitute/sting/utils/cmdLine/ParsingEngine.java index 6b6cecd4e..2e843da53 100755 --- a/java/src/org/broadinstitute/sting/utils/cmdLine/ParsingEngine.java +++ b/java/src/org/broadinstitute/sting/utils/cmdLine/ParsingEngine.java @@ -291,8 +291,8 @@ public class ParsingEngine { /** * Prints out the help associated with these command-line argument definitions. */ - public void printHelp( String runningInstructions ) { - new HelpFormatter().printHelp(runningInstructions,argumentDefinitions); + public void printHelp( ApplicationDetails applicationDetails ) { + new HelpFormatter().printHelp(applicationDetails,argumentDefinitions); } /**