/** * Copyright Copyright 2010-14 Simon Andrews * * This file is part of BamQC. * * BamQC is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * BamQC is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with BamQC; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Changelog: * - Piero Dalle Pezze: added annotation, edited runMappedFiles * - Simon Andrews: Class creation. */ package uk.ac.babraham.BamQC.Analysis; import java.io.File; import java.io.IOException; import java.util.Vector; import java.util.concurrent.atomic.AtomicInteger; import org.apache.log4j.Logger; import uk.ac.babraham.BamQC.BamQCConfig; import uk.ac.babraham.BamQC.Modules.ModuleFactory; import uk.ac.babraham.BamQC.Modules.QCModule; import uk.ac.babraham.BamQC.Report.HTMLReportArchive; import uk.ac.babraham.BamQC.Sequence.SequenceFactory; import uk.ac.babraham.BamQC.Sequence.SequenceFile; import uk.ac.babraham.BamQC.Sequence.SequenceFormatException; /** * * @author Simon Andrews * @author Piero Dalle Pezze * */ public class OfflineRunner implements AnalysisListener { private static Logger log = Logger.getLogger(OfflineRunner.class); private AtomicInteger filesRemaining; private boolean showUpdates = true; public OfflineRunner (String[] filenames) { // See if we need to show updates showUpdates = !BamQCConfig.getInstance().quiet; // a simple parser String bamqcUsageError = "The inserted parameters are not correct. Please use option -h (or --help) for help."; if(filenames.length == 0) { // no parameter. Just for completeness, as we will generally start the GUI if no parameter is passed. System.out.println(bamqcUsageError); } else if(BamQCConfig.getInstance().gff_file != null) { System.out.println("Annotation file: " + BamQCConfig.getInstance().gff_file.getAbsolutePath()); } else if(BamQCConfig.getInstance().genome != null) { System.out.println("Genome: " + BamQCConfig.getInstance().genome.getAbsolutePath()); } runMappedFiles(filenames); } public boolean isMappedFile(String bamFile) { if(bamFile.toLowerCase().endsWith(".sam") || bamFile.toLowerCase().endsWith(".bam")) { return true; } return false; } public void runMappedFiles(String[] bamfiles) { Vector<File> files = new Vector<File>(); // We make a special case if they supply a single filename // which is stdin. In this case we'll take data piped to us // rather than trying to read the actual file. We'll also // skip the existence check. if (bamfiles.length == 1 && bamfiles[0].equals("stdin")) { files.add(new File("stdin")); } else { for (int i=0;i<bamfiles.length;i++) { // first control File file = new File(bamfiles[i]); if (!file.exists() || ! file.canRead()) { log.warn("Skipping '"+file.getAbsolutePath()+"' which didn't exist, or couldn't be read"); continue; } // if we have a directory, let's see whether we have mapped files inside. If so, load them if(file.isDirectory()) { File[] subdirFiles = file.listFiles(); for(int j=0; j<subdirFiles.length; j++) { if(!isMappedFile(subdirFiles[j].getName())) { log.warn("Skipping '"+subdirFiles[j].getAbsolutePath()+"' as not a .sam or .bam file"); continue; } files.add(subdirFiles[j]); } } // we have a file. if this is a mapped file, load it. else { if(!isMappedFile(file.getName())) { log.warn("Skipping '"+file.getAbsolutePath()+"' as not a .sam or .bam file"); continue; } files.add(file); } } } // See if we need to group together files from a casava group filesRemaining = new AtomicInteger(files.size()); for (int i=0;i<files.size();i++) { try { processFile(files.elementAt(i)); } catch (SequenceFormatException e) { log.error("Format error in "+files.elementAt(i) + " : " + e.getLocalizedMessage(), e); filesRemaining.decrementAndGet(); } catch (IOException e) { log.error("File "+files.elementAt(i) + " broken : " + e.getLocalizedMessage(), e); filesRemaining.decrementAndGet(); } catch (Exception e) { log.error("Failed to process "+files.elementAt(i), e); filesRemaining.decrementAndGet(); } } // We need to hold this class open as otherwise the main method // exits when it's finished. while (filesRemaining.intValue() > 0) { try { Thread.sleep(1500); } catch (InterruptedException e) {} } System.exit(0); } public void processFile (File file) throws SequenceFormatException, IOException { if (!file.getName().equals("stdin") && !file.exists()) { throw new IOException(file.getName()+" doesn't exist"); } SequenceFile sequenceFile = SequenceFactory.getSequenceFile(file); AnalysisRunner runner = new AnalysisRunner(sequenceFile); runner.addAnalysisListener(this); QCModule [] moduleList = ModuleFactory.getStandardModuleList(); runner.startAnalysis(moduleList); } @Override public void analysisComplete(SequenceFile file, QCModule[] results) { File reportFile; if (showUpdates) System.out.println("Analysis complete for "+file.name()); if (BamQCConfig.getInstance().output_dir != null) { String fileName = file.getFile().getName().replaceAll("\\.gz$","").replaceAll("\\.bz2$","").replaceAll("\\.txt$","").replaceAll("\\.fastq$", "").replaceAll("\\.fastq$", "").replaceAll("\\.csfastq$", "").replaceAll("\\.sam$", "").replaceAll("\\.bam$", "")+"_bamqc.html"; reportFile = new File(BamQCConfig.getInstance().output_dir+"/"+fileName); } else { reportFile = new File(file.getFile().getAbsolutePath().replaceAll("\\.gz$","").replaceAll("\\.bz2$","").replaceAll("\\.txt$","").replaceAll("\\.fastq$", "").replaceAll("\\.fq$", "").replaceAll("\\.csfastq$", "").replaceAll("\\.sam$", "").replaceAll("\\.bam$", "")+"_bamqc.html"); } try { new HTMLReportArchive(file, results, reportFile); } catch (Exception e) { analysisExceptionReceived(file, e); return; } filesRemaining.decrementAndGet(); } @Override public void analysisUpdated(SequenceFile file, int sequencesProcessed, int percentComplete) { if (percentComplete % 5 == 0) { if (percentComplete == 105) { if (showUpdates) System.out.println("It seems our guess for the total number of records wasn't very good. Sorry about that."); } if (percentComplete > 100) { if (showUpdates) System.out.println("Still going at "+percentComplete+"% complete for "+file.name()); } else { if (showUpdates) System.out.println("Approx "+percentComplete+"% complete for "+file.name()); } } } @Override public void analysisExceptionReceived(SequenceFile file, Exception e) { log.error("Failed to process file "+file.name(), e); filesRemaining.decrementAndGet(); } @Override public void analysisStarted(SequenceFile file) { if (showUpdates) System.out.println("Started analysis of "+file.name()); } }