package org.broadinstitute.sting.gatk;
import edu.mit.broad.picard.reference.ReferenceSequenceFile;
import edu.mit.broad.picard.reference.ReferenceSequenceFileFactory;
import net.sf.samtools.SAMFileReader;
import net.sf.samtools.SAMFileReader.ValidationStringency;
import org.apache.log4j.Logger;
import org.broadinstitute.sting.gatk.executive.MicroScheduler;
import org.broadinstitute.sting.gatk.refdata.ReferenceOrderedData;
import org.broadinstitute.sting.gatk.refdata.ReferenceOrderedDatum;
import org.broadinstitute.sting.gatk.traversals.*;
import org.broadinstitute.sting.gatk.walkers.*;
import org.broadinstitute.sting.utils.GenomeLoc;
import org.broadinstitute.sting.utils.StingException;
import org.broadinstitute.sting.utils.Utils;
import java.util.ArrayList;
import java.util.List;
import java.io.File;
public class GenomeAnalysisEngine {
// our instance of this genome analysis toolkit; it's used by other classes to extract the traversal engine
// TODO: public static without final tends to indicate we're thinking about this the wrong way
public static GenomeAnalysisEngine instance;
// our traversal engine
private TraversalEngine engine = null;
// the level of debugging we're using
public boolean DEBUGGING = false;
// our argument collection
private final GATKArgumentCollection argCollection;
/** Collection of output streams used by the walker. */
private OutputTracker outputTracker = null;
/** our log, which we want to capture anything from this class */
private static Logger logger = Logger.getLogger(GenomeAnalysisEngine.class);
/**
* our constructor, where all the work is done
*
* legacy traversal types are sent to legacyTraversal function; as we move more of the traversals to the
* new MicroScheduler class we'll be able to delete that function.
*
* @param args the argument collection, where we get all our setup information from
* @param my_walker the walker we're running with
*/
public GenomeAnalysisEngine(GATKArgumentCollection args, Walker my_walker) {
// validate our parameters
if (args == null || my_walker == null) {
throw new StingException("Neither the GATKArgumentCollection or the Walker passed to GenomeAnalysisEngine can be null.");
}
// save our argument parameter
this.argCollection = args;
// make sure our instance variable points to this analysis engine
instance = this;
// our reference ordered data collection
List> rods = new ArrayList>();
//
// please don't use these in the future, use the new syntax <- if we're not using these please remove them
//
if (argCollection.DBSNPFile != null) bindConvenienceRods("dbSNP", "dbsnp", argCollection.DBSNPFile);
if (argCollection.HAPMAPFile != null)
bindConvenienceRods("hapmap", "HapMapAlleleFrequencies", argCollection.HAPMAPFile);
if (argCollection.HAPMAPChipFile != null)
bindConvenienceRods("hapmap-chip", "GFF", argCollection.HAPMAPChipFile);
// parse out the rod bindings
ReferenceOrderedData.parseBindings(logger, argCollection.RODBindings, rods);
// create the output streams
initializeOutputStreams( my_walker );
// our microscheduler, which is in charge of running everything
MicroScheduler microScheduler = null;
// if we're a read or a locus walker, we use the new system. Right now we have complicated
// branching based on the input data, but this should disapear when all the traversals are switched over
if ((my_walker instanceof LocusWalker && argCollection.walkAllLoci && !(argCollection.samFiles == null || argCollection.samFiles.size() == 0)) ||
my_walker instanceof ReadWalker) {
microScheduler = createMicroscheduler(my_walker, rods);
} else { // we have an old style traversal, once we're done return
legacyTraversal(my_walker, rods);
return;
}
// Prepare the sort ordering w.r.t. the sequence dictionary
if (argCollection.referenceFile != null) {
final ReferenceSequenceFile refFile = ReferenceSequenceFileFactory.getReferenceSequenceFile(argCollection.referenceFile);
GenomeLoc.setupRefContigOrdering(refFile);
}
// Determine the validation stringency. Default to ValidationStringency.STRICT.
ValidationStringency strictness = getValidationStringency();
logger.info("Strictness is " + strictness);
// perform validation steps that are common to all the engines
genericEngineSetup(strictness);
// parse out any genomic location they've provided
List locs = setupIntervalRegion();
// excute the microscheduler
microScheduler.execute(my_walker, locs);
}
/**
* this is to accomdate the older style traversals, that haven't been converted over to the new system. Putting them
* into their own function allows us to deviate in the two behaviors so the new style traversals aren't limited to what
* the old style does. As traversals are converted, this function should disappear.
*
* @param my_walker
* @param rods
*/
private void legacyTraversal(Walker my_walker, List> rods) {
if (my_walker instanceof LocusWindowWalker) {
this.engine = new TraverseByLocusWindows(argCollection.samFiles, argCollection.referenceFile, rods);
} else if (my_walker instanceof LocusWalker) {
if (argCollection.referenceFile == null)
Utils.scareUser(String.format("Locus-based traversals require a reference file but none was given"));
if (argCollection.samFiles == null || argCollection.samFiles.size() == 0) {
if (((LocusWalker) my_walker).requiresReads())
Utils.scareUser(String.format("Analysis %s requires reads, but none were given", argCollection.analysisName));
this.engine = new TraverseByReference(null, argCollection.referenceFile, rods);
} else {
if (((LocusWalker) my_walker).cannotHandleReads())
Utils.scareUser(String.format("Analysis %s doesn't support SAM/BAM reads, but a read file %s was provided", argCollection.analysisName, argCollection.samFiles));
this.engine = new TraverseByLoci(argCollection.samFiles, argCollection.referenceFile, rods);
}
} else if (my_walker instanceof DuplicateWalker) {
// we're a duplicate walker
this.engine = new TraverseDuplicates(argCollection.samFiles, argCollection.referenceFile, rods);
} else {
throw new RuntimeException("Unexpected walker type: " + my_walker);
}
// Prepare the sort ordering w.r.t. the sequence dictionary
if (argCollection.referenceFile != null) {
final ReferenceSequenceFile refFile = ReferenceSequenceFileFactory.getReferenceSequenceFile(argCollection.referenceFile);
GenomeLoc.setupRefContigOrdering(refFile);
}
// Determine the validation stringency. Default to ValidationStringency.STRICT.
ValidationStringency strictness = getValidationStringency();
logger.info("Strictness is " + strictness);
genericEngineSetup(strictness);
engine.traverse(my_walker);
}
/**
* setup a microscheduler
*
* @param my_walker our walker of type LocusWalker
* @param rods the reference order data
* @return a new microscheduler
*/
private MicroScheduler createMicroscheduler(Walker my_walker, List> rods) {
// the mircoscheduler to return
MicroScheduler microScheduler = null;
// we need to verify different parameter based on the walker type
if (my_walker instanceof LocusWalker) {
// some warnings
if (argCollection.referenceFile == null)
Utils.scareUser(String.format("Locus-based traversals require a reference file but none was given"));
if (((LocusWalker) my_walker).cannotHandleReads())
Utils.scareUser(String.format("Analysis %s doesn't support SAM/BAM reads, but a read file %s was provided", argCollection.analysisName, argCollection.samFiles));
// create the MicroScheduler
microScheduler = MicroScheduler.create(my_walker, argCollection.samFiles, argCollection.referenceFile, rods, argCollection.numberOfThreads);
engine = microScheduler.getTraversalEngine();
}
else if (my_walker instanceof ReadWalker)
{
if (argCollection.referenceFile == null)
Utils.scareUser(String.format("Locus-based traversals require a reference file but none was given"));
microScheduler = MicroScheduler.create(my_walker, argCollection.samFiles, argCollection.referenceFile, rods, argCollection.numberOfThreads);
engine = microScheduler.getTraversalEngine();
}
return microScheduler;
}
/**
* commands that get executed for each engine, regardless of the type
*
* @param strictness our current strictness level
*/
private void genericEngineSetup(ValidationStringency strictness) {
engine.setStrictness(strictness);
engine.setMaxReads(Integer.parseInt(argCollection.maximumReads));
// we default interval files over the genome region strin
if (argCollection.intervals != null) {
engine.setLocation(setupIntervalRegion());
}
// hmm...
if (argCollection.maximumReadSorts != null) {
engine.setSortOnFly(Integer.parseInt(argCollection.maximumReadSorts));
}
if (argCollection.downsampleFraction != null) {
engine.setDownsampleByFraction(Double.parseDouble(argCollection.downsampleFraction));
}
if (argCollection.downsampleCoverage != null) {
engine.setDownsampleByCoverage(Integer.parseInt(argCollection.downsampleCoverage));
}
engine.setSafetyChecking(!argCollection.unsafe);
engine.setThreadedIO(argCollection.enabledThreadedIO);
engine.setWalkOverAllSites(argCollection.walkAllLoci);
engine.initialize();
}
/**
* setup the interval regions, from either the interval file of the genome region string
*
* @return a list of genomeLoc representing the interval file
*/
private List setupIntervalRegion() {
List locs;
if( new File(argCollection.intervals).exists() ) {
logger.info("Intervals argument specifies a file. Loading intervals from file.");
return GenomeLoc.IntervalFileToList(argCollection.intervals);
}
else {
logger.info("Intervals argument does not specify a file. Trying to parse it as a simple string.");
return GenomeLoc.parseGenomeLocs(argCollection.intervals);
}
}
/**
* Default to ValidationStringency.STRICT.
*
* @return the validation stringency
*/
private ValidationStringency getValidationStringency() {
ValidationStringency strictness;
try {
strictness = Enum.valueOf(ValidationStringency.class, argCollection.strictnessLevel);
}
catch (IllegalArgumentException ex) {
strictness = ValidationStringency.STRICT;
}
return strictness;
}
/**
* Convenience function that binds RODs using the old-style command line parser to the new style list for
* a uniform processing.
*
* @param name
* @param type
* @param file
*/
private void bindConvenienceRods(final String name, final String type, final String file) {
argCollection.RODBindings.add(Utils.join(",", new String[]{name, type, file}));
}
/** Initialize the output streams as specified by the user. */
private void initializeOutputStreams( Walker walker ) {
outputTracker = (argCollection.outErrFileName != null) ? new OutputTracker(argCollection.outErrFileName, argCollection.outErrFileName)
: new OutputTracker(argCollection.outFileName, argCollection.errFileName);
walker.initializeOutputStreams(outputTracker);
}
/**
* Gets the output tracker. Tracks data available to a given walker.
*
* @return The output tracker.
*/
public OutputTracker getOutputTracker() {
return outputTracker;
}
public SAMFileReader getSamReader() {
return this.engine.getSamReader();
}
public TraversalEngine getEngine() {
return this.engine;
}
}