diff --git a/.gitignore b/.gitignore index 2c42ad628..f052ee115 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /.gradle/ /build/ .metadata +.idea diff --git a/src/main/java/scriptmanager/cli/BAM_Manipulation/BAMRemoveDupCLI.java b/src/main/java/scriptmanager/cli/BAM_Manipulation/BAMRemoveDupCLI.java index b571e3b79..dd0fd669e 100644 --- a/src/main/java/scriptmanager/cli/BAM_Manipulation/BAMRemoveDupCLI.java +++ b/src/main/java/scriptmanager/cli/BAM_Manipulation/BAMRemoveDupCLI.java @@ -2,6 +2,7 @@ import picocli.CommandLine.Command; +import java.io.File; import java.util.concurrent.Callable; import scriptmanager.objects.ToolDescriptions; @@ -29,4 +30,13 @@ public Integer call() throws Exception { System.exit(1); return(1); } + + public static String getCLIcommand(File BAM, boolean removeDuplicates, File OUTPUT, File METRICS) { + String command = "java -jar $PICARD MarkDuplicates"; + command += " INPUT=" + BAM.getAbsolutePath(); + command += " OUTPUT=" + OUTPUT.getAbsolutePath(); + command += " METRICS_FILE=" + METRICS.getAbsolutePath(); + command += removeDuplicates ? "REMOVE_DUPLICATES=true" : "REMOVE_DUPLICATES=false"; + return(command); + } } \ No newline at end of file diff --git a/src/main/java/scriptmanager/cli/BAM_Statistics/CrossCorrelationCLI.java b/src/main/java/scriptmanager/cli/BAM_Statistics/CrossCorrelationCLI.java new file mode 100644 index 000000000..4aeedf3a5 --- /dev/null +++ b/src/main/java/scriptmanager/cli/BAM_Statistics/CrossCorrelationCLI.java @@ -0,0 +1,126 @@ +package scriptmanager.cli.BAM_Statistics; + +import java.io.File; +import java.io.IOException; +import java.util.concurrent.Callable; + +import picocli.CommandLine.ArgGroup; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; +import scriptmanager.objects.ToolDescriptions; +import scriptmanager.objects.ArchTEx.CorrParameter; +import scriptmanager.scripts.BAM_Statistics.CrossCorrelation; +import scriptmanager.util.ExtensionFileFilter; + +/** + * Command line interface class for performing the ArchTEX cross correlation + * analysis by calling a method implemented in the scripts package. + * + * @author Olivia Lang + * @see scriptmanager.objects.ArchTEx.CorrParameter + * @see scriptmanager.scripts.BAM_Statistics.CrossCorrelation + */ +@Command(name = "cross-corr", mixinStandardHelpOptions = true, + description = ToolDescriptions.archtex_crosscorrelation_description, + version = "ScriptManager "+ ToolDescriptions.VERSION, + sortOptions = false, + exitCodeOnInvalidInput = 1, + exitCodeOnExecutionException = 1) +public class CrossCorrelationCLI implements Callable { + + @Parameters( index = "0", description = "The BAM file to perform the cross-correlation on") + private File bamFile; + + @Option(names = {"-o", "--output"}, description = "specify output basename, default is the BAM input filename without extension") + private File outputBasename = null; + + @ArgGroup(exclusive = true, multiplicity = "0..1", heading = "%nSelect correlation strategy:%n\t@|fg(red) (select no more than one of these options)|@%n") + CorrType corrType = new CorrType(); + static class CorrType { + @Option(names = {"-g", "--genome"}, description = "Use the full genome correlation method") + private boolean corrGenome = false; + @Option(names = {"-r", "--random"}, description = "Use the random sampling correlation method (default)") + private boolean corrRandom = false; + } + + @Option(names = {"-t", "--cpu"}, description = "set number of threads for performance tuning (default=1)") + private int cpu = 1; + + @ArgGroup(exclusive = true, multiplicity = "0..1", heading = "%nRandom Sampling Options:%n\t@|fg(red) (ignored if full genome correlation method selected)|@%n") + SamplingParams samplingParams = new SamplingParams(); + static class SamplingParams { + @Option(names = {"-w", "--window"}, description = "set window frame size for each extraction (default=50kb)") + private int windowSize = 50000; + @Option(names = {"-i", "--iterations"}, description = "set number of random iterations per chromosome (default=10)") + private int iterations = 10; + } + + CorrParameter param = new CorrParameter(); + + @Override + public Integer call() throws Exception { + System.err.println( ">CrossCorrelationCLI.call()" ); + String validate = validateInput(); + if(!validate.equals("")){ + System.err.println( validate ); + System.err.println("Invalid input. Check usage using '-h' or '--help'"); + System.exit(1); + } + + CrossCorrelation.correlate( outputBasename, bamFile, param, null); + + System.err.println("Calculations Complete"); + return(0); + } + + /** + * Validate the input values before executing the script. + * + * @return a multi-line string describing input validation issues + * @throws IOException + */ + private String validateInput() throws IOException { + String r = ""; + + //check inputs exist + if(!bamFile.exists()){ + r += "(!)BAM file does not exist: " + bamFile.getName() + "\n"; + return(r); + } + //check BAI exists + File f = new File(bamFile+".bai"); + if(!f.exists() || f.isDirectory()){ + r += "(!)BAI Index File does not exist for: " + bamFile.getName() + "\n"; + } + //set default output filename + if(outputBasename==null){ +// output = new File("output_bam_stats.txt"); //this default name mimics the gui + outputBasename = new File(ExtensionFileFilter.stripExtension(bamFile) + "_CrossCorrelation.txt"); + //check output filename is valid + }else{ + //no check ext + //check directory + if(outputBasename.getParent()==null){ +// System.err.println("default to current directory"); + } else if(!new File(outputBasename.getParent()).exists()){ + r += "(!)Check output directory exists: " + outputBasename.getParent() + "\n"; + } + } + + //validate random sampling params + if (corrType.corrGenome) { + param.setCorrType(true); + } else { + param.setCorrType(false); + if (samplingParams.windowSize<1) { r += "(!)Window size must be at least 1\n"; } + if (samplingParams.iterations<1) { r += "(!)Num iterations must be at least 1\n"; } + param.setWindow(samplingParams.windowSize); + } + // valiate CPU + if (cpu<1) { r += "(!)CPU count must be at least 1\n"; } + param.setThreads(cpu); + + return(r); + } +} diff --git a/src/main/java/scriptmanager/cli/Coordinate_Manipulation/BED_Manipulation/BEDtoGFFCLI.java b/src/main/java/scriptmanager/cli/Coordinate_Manipulation/BED_Manipulation/BEDtoGFFCLI.java index 652d09c31..9822b2f70 100644 --- a/src/main/java/scriptmanager/cli/Coordinate_Manipulation/BED_Manipulation/BEDtoGFFCLI.java +++ b/src/main/java/scriptmanager/cli/Coordinate_Manipulation/BED_Manipulation/BEDtoGFFCLI.java @@ -82,4 +82,12 @@ private String validateInput() throws IOException { return(r); } + + public static String getCLIcommand(File OUTPUT, File BED, boolean gzOutput) { + String command = "java -jar $SCRIPTMANAGER coordinate-manipulation bed-to-gff"; + command += " " + BED.getAbsolutePath(); + command += gzOutput ? " -z" : ""; + command += " -o " + OUTPUT; + return(command); + } } diff --git a/src/main/java/scriptmanager/cli/Read_Analysis/TagPileupCLI.java b/src/main/java/scriptmanager/cli/Read_Analysis/TagPileupCLI.java index a94d3636f..796a074b1 100644 --- a/src/main/java/scriptmanager/cli/Read_Analysis/TagPileupCLI.java +++ b/src/main/java/scriptmanager/cli/Read_Analysis/TagPileupCLI.java @@ -110,6 +110,8 @@ static class CalcOptions{ private int shift = 0; @Option(names = {"-b", "--bin-size"}, description = "set a bin size for the output (default=1bp)") private int binSize = 1; + @Option(names = {"-e", "--tag-extend"}, description = "set a bp length to extend the tag (default=0)") + private int tagExtend = 0; @Option(names = {"-t", "--standard"}, description = "set tags to be equal (default=false)") private boolean tagsEqual; @Option(names = {"--cpu"}, description = "set number of CPUs to use (default=1)") @@ -275,6 +277,7 @@ private String validateInput() throws IOException { //Set SHIFT, BIN, CPU p.setShift(calcOptions.shift); p.setBin(calcOptions.binSize); + p.setTagExtend(calcOptions.tagExtend); p.setCPU(calcOptions.cpu); //Set BLACKLIST & STANDARD @@ -298,4 +301,13 @@ private String validateInput() throws IOException { return(r); } + public static String getCLIcommand(File BED, File BAM, PileupParameters PARAM) { + String command = "java -jar $SCRIPTMANAGER read-analysis tag-pileup"; + command += " " + PARAM.getCLIOptions(); + command += " " + BED.getAbsolutePath(); + command += " " + BAM.getAbsolutePath(); + String NAME = PARAM.getOutputDirectory() + File.separator + PARAM.generateFileBase(BED.getName(), BAM.getName()); + command += " -M " + NAME + " -o " + NAME; + return(command); + } } \ No newline at end of file diff --git a/src/main/java/scriptmanager/main/LogManagerWindow.java b/src/main/java/scriptmanager/main/LogManagerWindow.java new file mode 100644 index 000000000..13322c728 --- /dev/null +++ b/src/main/java/scriptmanager/main/LogManagerWindow.java @@ -0,0 +1,385 @@ +package scriptmanager.main; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.Color; +import java.awt.Font; +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; + +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JScrollPane; +import javax.swing.JTabbedPane; +import javax.swing.JTable; +import javax.swing.JTextField; +import javax.swing.SpringLayout; +import javax.swing.border.EmptyBorder; +import javax.swing.table.DefaultTableModel; + +//import objects.PileupParameters; +//import objects.ToolDescriptions; +import scriptmanager.util.FileSelection; +import scriptmanager.objects.LogItem; +import scriptmanager.objects.ToolDescriptions; + +/** + * Store, update, and manage the set of LogItems that make up a ScriptManager + * session log with GUI management of log properties and features. + * + * @author Olivia Lang + * @see scriptmanager.main.ScriptManagerGUI + */ +@SuppressWarnings("serial") +public class LogManagerWindow extends JFrame { + +// private JPanel contentPane; + protected JFileChooser fc = new JFileChooser(new File(System.getProperty("user.dir"))); + private File OUT_DIR = new File(System.getProperty("user.dir")); + + private JTabbedPane tabbedPane; + + private String[] column = {"Status","Command","Start Time", "Stop Time"};; + + private JPanel pnl_Settings; + private JButton btnOutputDirectory; + private JLabel lblDefaultToLocal; + private JTextField txtOutputFilename; + + private JRadioButton verbosity1; + private JRadioButton verbosity2; + private JRadioButton verbosity3; + + private JButton btnWriteShell; + + private JPanel pnl_ViewLog; + private JButton btnClearAll; + private JButton btnDeleteSelected; + + private JTable tbl_Log; + + private ArrayList logItems; + private Timestamp logStart; + private int verbosity = 0; + + private File odir = null; + private String ofilename; + + private boolean on = true; + + public final LogItem[] test = { new LogItem("Some Command 1"), new LogItem("Some Command 2")}; + + /** + * Initialize log information and window + */ + public LogManagerWindow() { + // Initilize log timestamp + logStart = new Timestamp(new Date().getTime()); + logItems = new ArrayList(); + ofilename = logStart.toString().replace(' ', '_').replace(':', '-') + "_logfile.sh"; + initialize(); + } + + /** + * Initialize window components for managing log properties and content + */ + public void initialize() { + setTitle("Logging Manager"); + setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + setBounds(125, 125, 650, 350); + + tabbedPane = new JTabbedPane(JTabbedPane.TOP); + setContentPane(tabbedPane); + SpringLayout sl_tabbedPane = new SpringLayout(); + + sl_tabbedPane.putConstraint(SpringLayout.NORTH, tabbedPane, 10, SpringLayout.NORTH, getContentPane()); + sl_tabbedPane.putConstraint(SpringLayout.WEST, tabbedPane, 10, SpringLayout.WEST, getContentPane()); + sl_tabbedPane.putConstraint(SpringLayout.SOUTH, tabbedPane, -10, SpringLayout.SOUTH, getContentPane()); + sl_tabbedPane.putConstraint(SpringLayout.EAST, tabbedPane, -10, SpringLayout.EAST, getContentPane()); + + // >>>>>>>> Settings <<<<<<<< + pnl_Settings = new JPanel(); + pnl_Settings.setBorder(new EmptyBorder(5, 5, 5, 5)); + SpringLayout sl_pnlSettings = new SpringLayout(); + pnl_Settings.setLayout(sl_pnlSettings); + tabbedPane.addTab("Log Settings", null, pnl_Settings, null); + + JCheckBox chckbxToggleLog = new JCheckBox("Toggle Logging"); + sl_pnlSettings.putConstraint(SpringLayout.NORTH, chckbxToggleLog, 10, SpringLayout.NORTH, pnl_Settings); + sl_pnlSettings.putConstraint(SpringLayout.WEST, chckbxToggleLog, 10, SpringLayout.WEST, pnl_Settings); + chckbxToggleLog.setSelected(on); + chckbxToggleLog.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + setToggleOn(chckbxToggleLog.isSelected()); + } + }); + pnl_Settings.add(chckbxToggleLog); + + // Define Verbosity + JLabel lblVerbosity = new JLabel("Verbosity:"); + sl_pnlSettings.putConstraint(SpringLayout.NORTH, lblVerbosity, 10, SpringLayout.SOUTH, chckbxToggleLog); + sl_pnlSettings.putConstraint(SpringLayout.WEST, lblVerbosity, 10, SpringLayout.WEST, pnl_Settings); + pnl_Settings.add(lblVerbosity); + + verbosity1 = new JRadioButton("1"); + sl_pnlSettings.putConstraint(SpringLayout.NORTH, verbosity1, 0, SpringLayout.NORTH, lblVerbosity); + sl_pnlSettings.putConstraint(SpringLayout.WEST, verbosity1, 10, SpringLayout.EAST, lblVerbosity); + pnl_Settings.add(verbosity1); + + verbosity2 = new JRadioButton("2"); + sl_pnlSettings.putConstraint(SpringLayout.NORTH, verbosity2, 0, SpringLayout.NORTH, lblVerbosity); + sl_pnlSettings.putConstraint(SpringLayout.WEST, verbosity2, 10, SpringLayout.EAST, verbosity1); + pnl_Settings.add(verbosity2); + + verbosity3 = new JRadioButton("3"); + sl_pnlSettings.putConstraint(SpringLayout.NORTH, verbosity3, 0, SpringLayout.NORTH, lblVerbosity); + sl_pnlSettings.putConstraint(SpringLayout.WEST, verbosity3, 10, SpringLayout.EAST, verbosity2); + pnl_Settings.add(verbosity3); + + ButtonGroup verbosity = new ButtonGroup(); + verbosity.add(verbosity1); + verbosity.add(verbosity2); + verbosity.add(verbosity3); + verbosity2.setSelected(true); + + // Output Settings + btnOutputDirectory = new JButton("Output Directory"); + sl_pnlSettings.putConstraint(SpringLayout.NORTH, btnOutputDirectory, 10, SpringLayout.SOUTH, lblVerbosity); + sl_pnlSettings.putConstraint(SpringLayout.WEST, btnOutputDirectory, 10, SpringLayout.WEST, pnl_Settings); + pnl_Settings.add(btnOutputDirectory); + + JLabel lblCurrentOutput = new JLabel("Current Output:"); + sl_pnlSettings.putConstraint(SpringLayout.NORTH, lblCurrentOutput, 10, SpringLayout.SOUTH, btnOutputDirectory); + sl_pnlSettings.putConstraint(SpringLayout.WEST, lblCurrentOutput, 0, SpringLayout.WEST, btnOutputDirectory); + lblCurrentOutput.setFont(new Font("Lucida Grande", Font.BOLD, 13)); + pnl_Settings.add(lblCurrentOutput); + + lblDefaultToLocal = new JLabel("Default to Local Directory"); + if(getOutputDirectory() != null) { + lblDefaultToLocal = new JLabel(getOutputDirectory().getAbsolutePath()); + } + sl_pnlSettings.putConstraint(SpringLayout.NORTH, lblDefaultToLocal, 0, SpringLayout.NORTH, lblCurrentOutput); + sl_pnlSettings.putConstraint(SpringLayout.WEST, lblDefaultToLocal, 10, SpringLayout.EAST, lblCurrentOutput); + lblDefaultToLocal.setFont(new Font("Dialog", Font.PLAIN, 12)); + lblDefaultToLocal.setBackground(Color.WHITE); + pnl_Settings.add(lblDefaultToLocal); + + btnOutputDirectory.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + File temp = FileSelection.getOutputDir(fc); + if (temp != null) { + OUT_DIR = temp; + lblDefaultToLocal.setText(temp.getAbsolutePath()); + } + } + }); + + JLabel lblFileSeparator = new JLabel(File.separator); + sl_pnlSettings.putConstraint(SpringLayout.NORTH, lblFileSeparator, 10, SpringLayout.SOUTH, lblCurrentOutput); + sl_pnlSettings.putConstraint(SpringLayout.WEST, lblFileSeparator, 10, SpringLayout.WEST, pnl_Settings); + pnl_Settings.add(lblFileSeparator); + + txtOutputFilename = new JTextField(); + sl_pnlSettings.putConstraint(SpringLayout.NORTH, txtOutputFilename, 0, SpringLayout.NORTH, lblFileSeparator); + sl_pnlSettings.putConstraint(SpringLayout.WEST, txtOutputFilename, 10, SpringLayout.EAST, lblFileSeparator); + sl_pnlSettings.putConstraint(SpringLayout.EAST, txtOutputFilename, -10, SpringLayout.EAST, pnl_Settings); + txtOutputFilename.setText(getFilename()); + pnl_Settings.add(txtOutputFilename); + txtOutputFilename.setColumns(100); + + btnWriteShell = new JButton("Write Log to shell script"); + sl_pnlSettings.putConstraint(SpringLayout.SOUTH, btnWriteShell, -10, SpringLayout.SOUTH, pnl_Settings); + sl_pnlSettings.putConstraint(SpringLayout.WEST, btnWriteShell, 10, SpringLayout.WEST, pnl_Settings); + btnWriteShell.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + try { + writeLogShell(); + } catch (IOException ioe) { + JOptionPane.showMessageDialog(null, ioe.getMessage()); + } + } + }); + pnl_Settings.add(btnWriteShell); + + // >>>>>>>> View <<<<<<<< + pnl_ViewLog = new JPanel(); + pnl_ViewLog.setBorder(new EmptyBorder(5, 5, 5, 5)); + SpringLayout sl_pnlViewLog = new SpringLayout(); + pnl_ViewLog.setLayout(sl_pnlViewLog); +// setContentPane(pnl_ViewLog); + tabbedPane.addTab("View Log", null, pnl_ViewLog, null); + + // ScrollPane to hold the JTable + tbl_Log = new JTable(new String[0][3], column); + updateTable(); +// tbl_Log.setBounds(30,40,200,300); + + JScrollPane scrollPane = new JScrollPane(tbl_Log); + sl_pnlViewLog.putConstraint(SpringLayout.WEST, scrollPane, 10, SpringLayout.WEST, pnl_ViewLog); + sl_pnlViewLog.putConstraint(SpringLayout.NORTH, scrollPane, -50, SpringLayout.NORTH, pnl_ViewLog); + sl_pnlViewLog.putConstraint(SpringLayout.SOUTH, scrollPane, -10, SpringLayout.SOUTH, pnl_ViewLog); + sl_pnlViewLog.putConstraint(SpringLayout.EAST, scrollPane, -10, SpringLayout.EAST, pnl_ViewLog); + pnl_ViewLog.add(scrollPane); + + + btnClearAll = new JButton("Clear All"); + sl_pnlViewLog.putConstraint(SpringLayout.NORTH, btnClearAll, 5, SpringLayout.NORTH, pnl_ViewLog); + sl_pnlViewLog.putConstraint(SpringLayout.NORTH, scrollPane, 10, SpringLayout.SOUTH, btnClearAll); + sl_pnlViewLog.putConstraint(SpringLayout.EAST, btnClearAll, -10, SpringLayout.EAST, pnl_ViewLog); + btnClearAll.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + clearLog(); + updateTable(); + } + }); + pnl_ViewLog.add(btnClearAll); + + btnDeleteSelected = new JButton("Delete Selection"); + sl_pnlViewLog.putConstraint(SpringLayout.NORTH, btnDeleteSelected, 0, SpringLayout.NORTH, btnClearAll); + sl_pnlViewLog.putConstraint(SpringLayout.EAST, btnDeleteSelected, -10, SpringLayout.WEST, btnClearAll); + btnDeleteSelected.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + int[] selection = tbl_Log.getSelectedRows(); + removeLogItems(selection); + updateTable(); + } + }); + pnl_ViewLog.add(btnDeleteSelected); + } + + /** + * Update the JTable from the logItems ArrayList + */ + public void updateTable() { + int n = logItems.size(); + String[][] d = new String[n][3]; + for(int i = 0; i getLogItems() { return logItems; } + public LogItem getLogItem(int i) { return logItems.get(i); } + public Timestamp getLogStart() { return logStart; } + public int getVerbosity() { return verbosity; } + public String getFilename() { return ofilename; } + public File getOutputDirectory() { return odir; } + + // Setters + public void setToggleOn(boolean toggle) { on = toggle; } + public void setLogStart(Timestamp time) { logStart = time; } + public void setVerbosity(int v) { verbosity = v; } + public void setFilename(String o) { ofilename = o; } + public void setOutputDirectory(File o) { odir = o; } + + // Status - getters, setters, and Comparators + // Comparator (timestamp) + + /** + * Clear empty the logItems ArrayList + */ + public void clearLog() { + logItems = new ArrayList(0); + logStart = new Timestamp(new Date().getTime()); + } + + /** + * Append LogItem to the end of the logItems ArrayList and update the JTable + * + * @param li the entry to append to logItems + */ + public void addLogItem(LogItem li) { + logItems.add(li); + } + + /** + * Remove LogItems from the Log indicated by an array of indices + * + * @param r_idxs index list corresponding to LogItem indices in the `logItems` + * ArrayList + */ + public void removeLogItems(int[] r_idxs) { + Arrays.sort(r_idxs); + for (int i = r_idxs.length; i>0; i--) { + logItems.remove(r_idxs[i-1]); + } + } + + /** + * Write the contents of the log as a shell script + * + * @throws IOException + */ + public void writeLogShell() throws IOException { + // Create File & initialize stream + PrintStream OUT = new PrintStream(new File(OUT_DIR + File.separator + ofilename)); + + // Write Header (V=1+) + OUT.println("# VERSION\t" + ToolDescriptions.VERSION); + OUT.println("SCRIPTMANAGER=/path/to/ScriptManager.jar"); + OUT.println("PICARD=/path/to/picard.jar"); + OUT.println(); + + // Write LogInfo start time (V=1+) + OUT.println("# Log start time: " + logStart.toString()); + + // Sort LogItems for writing--not needed unless fireproperty adding out of order + + // Record each LogItem + for (int i = 0; i=2) { + // Write LogItem start time (V=2+) + OUT.println("# " + item.getStartTime().toString()); + } + + // Write LogItem command string (V=1+) + OUT.println(item.getCommand()); + + if(verbosity>=2) { + // Write LogItem status (V=2+) + OUT.println("# " + item.getStatus()); + // Write LogItem end time (V=2+) + OUT.println("# " + item.getStopTime().toString()); + // Write LogItem runtime (V=3+) + if(verbosity>=3) { + OUT.println("# Runtime: " + (item.getStopTime().getTime() - item.getStartTime().getTime())); + } + } + // TODO: Write Toggle Log info + } + + // Write LogInfo write/save/end time (V=1+) + OUT.println(); + OUT.println("# Written at " + new Timestamp(new Date().getTime()).toString()); + + OUT.close(); + } +} diff --git a/src/main/java/scriptmanager/main/ScriptManager.java b/src/main/java/scriptmanager/main/ScriptManager.java index 689d334d3..e0b6e8d9a 100755 --- a/src/main/java/scriptmanager/main/ScriptManager.java +++ b/src/main/java/scriptmanager/main/ScriptManager.java @@ -18,6 +18,7 @@ import scriptmanager.cli.BAM_Manipulation.MergeBAMCLI; import scriptmanager.cli.BAM_Manipulation.SortBAMCLI; +import scriptmanager.cli.BAM_Statistics.CrossCorrelationCLI; import scriptmanager.cli.BAM_Statistics.BAMGenomeCorrelationCLI; import scriptmanager.cli.BAM_Statistics.PEStatsCLI; import scriptmanager.cli.BAM_Statistics.SEStatsCLI; @@ -42,7 +43,6 @@ import scriptmanager.cli.File_Utilities.MD5ChecksumCLI; import scriptmanager.cli.File_Utilities.ConvertBEDChrNamesCLI; import scriptmanager.cli.File_Utilities.ConvertGFFChrNamesCLI; - import scriptmanager.cli.Peak_Analysis.BEDPeakAligntoRefCLI; import scriptmanager.cli.Peak_Analysis.FilterBEDbyProximityCLI; import scriptmanager.cli.Peak_Analysis.RandomCoordinateCLI; @@ -141,11 +141,12 @@ class BAM_ManipulationCLI extends SubcommandCLI {} @Command(name = "bam-statistics", subcommands = { + CrossCorrelationCLI.class, BAMGenomeCorrelationCLI.class, PEStatsCLI.class, SEStatsCLI.class }, - description = "Includes tools like BAMGenomeCorrelationCLI, PEStatsCLI, and SEStatsCLI.") + description = "Includes tools like SEStatsCLI, PEStatsCLI, BAMGenomeCorrelationCLI, and CrossCorrelationCLI.") class BAM_StatisticsCLI extends SubcommandCLI {} diff --git a/src/main/java/scriptmanager/main/ScriptManagerGUI.java b/src/main/java/scriptmanager/main/ScriptManagerGUI.java index fe3a0d593..422eb6467 100755 --- a/src/main/java/scriptmanager/main/ScriptManagerGUI.java +++ b/src/main/java/scriptmanager/main/ScriptManagerGUI.java @@ -3,6 +3,8 @@ import java.awt.EventQueue; import java.awt.FlowLayout; import java.awt.event.ActionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.awt.event.ActionEvent; import javax.swing.JFrame; @@ -15,10 +17,11 @@ import javax.swing.JTextArea; import scriptmanager.objects.ToolDescriptions; - +import scriptmanager.objects.LogItem; import scriptmanager.window_interface.BAM_Statistics.PEStatWindow; import scriptmanager.window_interface.BAM_Statistics.SEStatWindow; import scriptmanager.window_interface.BAM_Statistics.BAMGenomeCorrelationWindow; +import scriptmanager.window_interface.BAM_Statistics.CrossCorrelationWindow; import scriptmanager.window_interface.BAM_Manipulation.BAIIndexerWindow; import scriptmanager.window_interface.BAM_Manipulation.BAMMarkDupWindow; import scriptmanager.window_interface.BAM_Manipulation.FilterforPIPseqWindow; @@ -63,8 +66,17 @@ import scriptmanager.window_interface.Figure_Generation.PlotCompositeWindow; import scriptmanager.window_interface.Figure_Generation.LabelHeatMapWindow; +/** + * Main GUI object that manages main window design for spinning out the GUI for + * each tool and the logging manager window. Instantiated by + * scriptmanager.main.ScriptManager. + * + * @author William KM Lai + * @see scriptmanager.main.ScriptManager + */ public class ScriptManagerGUI { private JFrame frmScriptManager; + private static LogManagerWindow logs; /** * Initialize the contents of the frame. @@ -72,21 +84,48 @@ public class ScriptManagerGUI { private void initialize() { frmScriptManager = new JFrame(); frmScriptManager.setTitle("Script Manager v" + ToolDescriptions.VERSION); - frmScriptManager.setBounds(100, 100, 600, 350); + frmScriptManager.setBounds(100, 100, 600, 380); frmScriptManager.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frmScriptManager.setResizable(false); SpringLayout springLayout = new SpringLayout(); frmScriptManager.getContentPane().setLayout(springLayout); + // >>>>>>>> Logging <<<<<<<< + JPanel pnlLogging = new JPanel(); + springLayout.putConstraint(SpringLayout.NORTH, pnlLogging, -40, SpringLayout.SOUTH, frmScriptManager.getContentPane()); + springLayout.putConstraint(SpringLayout.WEST, pnlLogging, 10, SpringLayout.WEST, frmScriptManager.getContentPane()); + springLayout.putConstraint(SpringLayout.SOUTH, pnlLogging, -10, SpringLayout.SOUTH, frmScriptManager.getContentPane()); + springLayout.putConstraint(SpringLayout.EAST, pnlLogging, -10, SpringLayout.EAST, frmScriptManager.getContentPane()); + frmScriptManager.getContentPane().add(pnlLogging); + +// TitledBorder ttlLogging = BorderFactory.createTitledBorder("Session logs"); +// ttlLogging.setTitleFont(new Font("Lucida Grande", Font.ITALIC, 14)); +// pnlLogging.setBorder(ttlLogging); + + // Initialize Logging Manager Window + logs = new LogManagerWindow(); + + JButton btnLoggingManager = new JButton("Open Log Manager"); + btnLoggingManager.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + EventQueue.invokeLater(new Runnable() { + public void run() { + try { + logs.setVisible(true); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + }); + pnlLogging.add(btnLoggingManager); + JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP); - springLayout.putConstraint(SpringLayout.NORTH, tabbedPane, 10, SpringLayout.NORTH, - frmScriptManager.getContentPane()); - springLayout.putConstraint(SpringLayout.WEST, tabbedPane, 10, SpringLayout.WEST, - frmScriptManager.getContentPane()); - springLayout.putConstraint(SpringLayout.SOUTH, tabbedPane, -10, SpringLayout.SOUTH, - frmScriptManager.getContentPane()); - springLayout.putConstraint(SpringLayout.EAST, tabbedPane, -10, SpringLayout.EAST, - frmScriptManager.getContentPane()); + springLayout.putConstraint(SpringLayout.NORTH, tabbedPane, 10, SpringLayout.NORTH, frmScriptManager.getContentPane()); + springLayout.putConstraint(SpringLayout.WEST, tabbedPane, 10, SpringLayout.WEST, frmScriptManager.getContentPane()); + springLayout.putConstraint(SpringLayout.SOUTH, tabbedPane, -10, SpringLayout.NORTH, pnlLogging); + springLayout.putConstraint(SpringLayout.EAST, tabbedPane, -10, SpringLayout.EAST, frmScriptManager.getContentPane()); frmScriptManager.getContentPane().add(tabbedPane); // >>>>>>>> BAM_Statistics <<<<<<<< @@ -176,13 +215,40 @@ public void run() { }); } }); - sl_pnlStat.putConstraint(SpringLayout.NORTH, btnBamGenomeCorrelation, 0, SpringLayout.NORTH, - txtBamGenomeCorrelation); + sl_pnlStat.putConstraint(SpringLayout.NORTH, btnBamGenomeCorrelation, 0, SpringLayout.NORTH, txtBamGenomeCorrelation); sl_pnlStat.putConstraint(SpringLayout.WEST, btnBamGenomeCorrelation, 10, SpringLayout.WEST, pnlStat); - sl_pnlStat.putConstraint(SpringLayout.WEST, txtBamGenomeCorrelation, 10, SpringLayout.EAST, - btnBamGenomeCorrelation); + sl_pnlStat.putConstraint(SpringLayout.WEST, txtBamGenomeCorrelation, 10, SpringLayout.EAST, btnBamGenomeCorrelation); pnlStat.add(btnBamGenomeCorrelation); + // >CrossCorrelation + JTextArea txtCrossCorrelation = new JTextArea(); + initializeTextArea(txtCrossCorrelation); + txtCrossCorrelation.setText(ToolDescriptions.archtex_crosscorrelation_description); + sl_pnlStat.putConstraint(SpringLayout.NORTH, txtCrossCorrelation, 10, SpringLayout.SOUTH, txtBamGenomeCorrelation); + sl_pnlStat.putConstraint(SpringLayout.EAST, txtCrossCorrelation, -10, SpringLayout.EAST, pnlStat); + pnlStat.add(txtCrossCorrelation); + + JButton btnCrossCorrelation = new JButton("Cross Correlation"); + btnCrossCorrelation.setToolTipText("Calculate optimal tag shift based on ArchTEx implementation (PMID:22302569)"); + btnCrossCorrelation.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent arg0) { + EventQueue.invokeLater(new Runnable() { + public void run() { + try { + CrossCorrelationWindow frame = new CrossCorrelationWindow(); + frame.setVisible(true); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + }); + sl_pnlStat.putConstraint(SpringLayout.NORTH, btnCrossCorrelation, 0, SpringLayout.NORTH, txtCrossCorrelation); + sl_pnlStat.putConstraint(SpringLayout.WEST, btnCrossCorrelation, 10, SpringLayout.WEST, pnlStat); + sl_pnlStat.putConstraint(SpringLayout.WEST, txtCrossCorrelation, 10, SpringLayout.EAST, btnCrossCorrelation); + pnlStat.add(btnCrossCorrelation); + // >>>>>>>> BAM_Manipulation <<<<<<<< JPanel pnlBamManip = new JPanel(); SpringLayout sl_pnlBamManip = new SpringLayout(); @@ -260,6 +326,17 @@ public void actionPerformed(ActionEvent e) { public void run() { try { BAMMarkDupWindow frame = new BAMMarkDupWindow(); + frame.addPropertyChangeListener("log", new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent evt) { + // Add log item if logging is turned on + if ("log" == evt.getPropertyName() && logs.getToggleOn()) { + if (evt.getNewValue() != null) { + logs.addLogItem((LogItem) evt.getNewValue()); + } + logs.updateTable(); + } + } + }); frame.setVisible(true); } catch (Exception e) { e.printStackTrace(); @@ -486,7 +563,7 @@ public void run() { sl_pnlFileUtility.putConstraint(SpringLayout.WEST, btnMD5, 10, SpringLayout.WEST, pnlFileUtility); sl_pnlFileUtility.putConstraint(SpringLayout.WEST, txtMD5, 10, SpringLayout.EAST, btnMD5); pnlFileUtility.add(btnMD5); - + // >ConvertBEDChrNames JTextArea txtConvertBEDChrNames = new JTextArea(); initializeTextArea(txtConvertBEDChrNames); @@ -494,7 +571,7 @@ public void run() { sl_pnlFileUtility.putConstraint(SpringLayout.NORTH, txtConvertBEDChrNames, 10, SpringLayout.SOUTH, btnMD5); sl_pnlFileUtility.putConstraint(SpringLayout.EAST, txtConvertBEDChrNames, -10, SpringLayout.EAST, pnlFileUtility); pnlFileUtility.add(txtConvertBEDChrNames); - + JButton btnConvertBEDChrNames = new JButton("Convert BED Chr Names"); btnConvertBEDChrNames.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { @@ -522,7 +599,7 @@ public void run() { sl_pnlFileUtility.putConstraint(SpringLayout.NORTH, txtConvertGFFChrNames, 10, SpringLayout.SOUTH, txtConvertBEDChrNames); sl_pnlFileUtility.putConstraint(SpringLayout.EAST, txtConvertGFFChrNames, -10, SpringLayout.EAST, pnlFileUtility); pnlFileUtility.add(txtConvertGFFChrNames); - + JButton btnConvertGFFChrNames = new JButton("Convert GFF Chr Names"); btnConvertGFFChrNames.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { @@ -906,6 +983,17 @@ public void actionPerformed(ActionEvent arg0) { public void run() { try { BEDtoGFFWindow frame = new BEDtoGFFWindow(); + frame.addPropertyChangeListener("log", new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent evt) { + // Add log item if logging is turned on + if ("log" == evt.getPropertyName() && logs.getToggleOn()) { + if (evt.getNewValue() != null) { + logs.addLogItem((LogItem) evt.getNewValue()); + } + logs.updateTable(); + } + } + }); frame.setVisible(true); } catch (Exception e) { e.printStackTrace(); @@ -993,8 +1081,8 @@ public void run() { } }); pnlCoordManip.add(btnShiftInterval); - - + + // >>>>>>>> Read_Analysis <<<<<<<< JPanel pnlReadAnalysis = new JPanel(); SpringLayout sl_pnlReadAnalysis = new SpringLayout(); @@ -1016,6 +1104,17 @@ public void actionPerformed(ActionEvent e) { public void run() { try { TagPileupWindow frame = new TagPileupWindow(); + frame.addPropertyChangeListener("log", new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent evt) { + // Add log item if logging is turned on + if ("log" == evt.getPropertyName() && logs.getToggleOn()) { + if (evt.getNewValue() != null) { + logs.addLogItem((LogItem) evt.getNewValue()); + } + logs.updateTable(); + } + } + }); frame.setVisible(true); } catch (Exception e) { e.printStackTrace(); @@ -1432,7 +1531,7 @@ public void run() { sl_pnlFigure.putConstraint(SpringLayout.WEST, btncolorSequencePlot, 10, SpringLayout.WEST, pnlFigure); sl_pnlFigure.putConstraint(SpringLayout.WEST, txtcolorSequencePlot, 10, SpringLayout.EAST, btncolorSequencePlot); pnlFigure.add(btncolorSequencePlot); - + // >FourColorPlot JTextArea txtMakeCompositePlot = new JTextArea(); @@ -1506,4 +1605,5 @@ public void run() { }); } + } diff --git a/src/main/java/scriptmanager/objects/ArchTEx/CorrExtract.java b/src/main/java/scriptmanager/objects/ArchTEx/CorrExtract.java new file mode 100644 index 000000000..6707f99c4 --- /dev/null +++ b/src/main/java/scriptmanager/objects/ArchTEx/CorrExtract.java @@ -0,0 +1,198 @@ +package scriptmanager.objects.ArchTEx; + +import java.io.File; +import java.io.IOException; +import java.util.Vector; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import htsjdk.samtools.SAMRecord; +import htsjdk.samtools.SamReader; +import htsjdk.samtools.SamReaderFactory; +import htsjdk.samtools.ValidationStringency; +import htsjdk.samtools.util.CloseableIterator; + +//import scriptmanager.objects.ArchTEx.CorrParameter; +//import net.sf.samtools.SAMFileReader; +//import net.sf.samtools.SAMRecord; +//import net.sf.samtools.util.CloseableIterator; + +/** + * Helper object for ArchTEx Cross-Correlation analysis to parse BAM files and + * extract tag counts to correlate into a CorrNode object. This is
+ * Code largely sourced from ArchTEx.analysis.corr.CorrExtract in https://github.com/WilliamKMLai/ArchTEx + * Primarily modified to use ScriptManager's version of htsjdk. + * + * @author William KM Lai + * @see scriptmanager.objects.ArchTEx.CorrParameter + * @see scriptmanager.scripts.BAM_Statistics.CrossCorrelation + */ +public class CorrExtract implements Runnable { + final Lock lock = new ReentrantLock(); + final Condition inUse = lock.newCondition(); + + File input; + CorrParameter Parameters; + Vector ALLNodes; + + int INDEX; + int SUBSETSIZE; + + static int progressCounter = 0; + static int currentProgress = 0; + + final SamReaderFactory factory = SamReaderFactory.makeDefault().enable(SamReaderFactory.Option.INCLUDE_SOURCE_IN_RECORDS, SamReaderFactory.Option.VALIDATE_CRC_CHECKSUMS).validationStringency(ValidationStringency.SILENT); + SamReader reader; +// SAMFileReader inputSam; + + /** + * Pass necessary parameters to perform extraction (multi-threaded) + * + * @param i the BAM file to extract pileups from + * @param param the stored Cross-Correlation parameters for how to perform + * extraction + * @param nodes the set of CorrNodes storing the extracted values + * @param current the start of the subset nodes in the list to extract for + * @param size the length or how many nodes from "current" in the node list to extract for + */ + public CorrExtract(File i, CorrParameter param, Vector nodes, int current, int size) { + input = i; + Parameters = param; + ALLNodes = nodes; + INDEX = current; + SUBSETSIZE = size; + } + + @Override + public void run() { + for(int x = INDEX; x < INDEX + SUBSETSIZE; x++) { + try { + reader = factory.open(input); +// AbstractBAMFileIndex bai = (AbstractBAMFileIndex) reader.indexing().getIndex(); +// inputSam = new SAMFileReader(input, new File(input.getAbsoluteFile() + ".bai")); + ALLNodes.get(x).setData(extractRegion(ALLNodes.get(x), Parameters)); +// inputSam.close(); + reader.close(); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + + //After tag data is added, calculate out statistics for the forward and reverse tags in the node + double[] Ftag = ALLNodes.get(x).getData().get(0); + double[] Rtag = ALLNodes.get(x).getData().get(1); + + //System.out.println(Ftag.length + "\t" + Rtag.length); + double count = 0, sx = 0, sxx = 0, sy = 0, syy = 0; + double[] sxy = new double[Parameters.getCorrWindow()]; + for(int winIndex = 0; winIndex < Ftag.length - Parameters.getCorrWindow(); winIndex++) { + count++; + sx += Ftag[winIndex]; + sxx += (Ftag[winIndex] * Ftag[winIndex]); + sy += Rtag[winIndex]; + syy += (Rtag[winIndex] * Rtag[winIndex]); + for(int shift = 0; shift < Parameters.getCorrWindow(); shift++) { + sxy[shift] += (Ftag[winIndex] * Rtag[winIndex + shift]); + } + } + + ALLNodes.get(x).setCount(count); + ALLNodes.get(x).setSx(sx); + ALLNodes.get(x).setSxx(sxx); + ALLNodes.get(x).setSy(sy); + ALLNodes.get(x).setSyy(syy); + ALLNodes.get(x).setSxy(sxy); + + //Remove residual tag data after analysis to conserve memory + ALLNodes.get(x).setData(null); + + progressCounter++; + int current = ((int)(0.5 + ((double)progressCounter / (double)ALLNodes.size() * 100))); + if(current > currentProgress) { + currentProgress = current; + System.out.print(current + "%" + "\t0 {"); + for(int bar = 1; bar <= 20; bar++) { + if(current >= bar * 5) { System.out.print("="); } + else { System.out.print(" "); } + } + System.out.print("} 100\r"); + System.out.flush(); + } + } + } + + /** + * Extract from BAM file for a specific node. + * + * @param current the specific node to extract read counts for + * @param param the parameter options used to configure extraction + * @return the list of forward and reverse tag count primitive arrays + */ + public Vector extractRegion(CorrNode current, CorrParameter param) { + int WINDOW = current.getStop() - current.getStart(); + if(WINDOW < 1) { + System.out.println(WINDOW + "\t" + current.getStop() + "\t" + current.getStart() + "\t" + param.getResolution()); + } + double[] Ftags = new double[(WINDOW / param.getResolution()) + 1]; + double[] Rtags = new double[(WINDOW / param.getResolution()) + 1]; + String CHROM = current.getChrom(); + int START = current.getStart(); + int STOP = current.getStop(); + int RESOLUTION = param.getResolution(); + + //Create an iterator for every tag overlapping this region. + try { +// CloseableIterator iter = inputSam.query(CHROM, START - 1, STOP + 1, false); + CloseableIterator iter = reader.query(CHROM, START - 1, STOP + 1, false); +// /* Iterate through the records that overlap this region. */ + while (iter.hasNext()) { + /* Create the record object */ + SAMRecord sr = iter.next(); + /* Get the start of the record */ + int recordStart = sr.getAlignmentStart(); + int windowMin = START; + int windowMax = STOP; + + if (sr.getReadNegativeStrandFlag()) { + recordStart = recordStart + sr.getReadLength() - 1; + recordStart += 10; + for(int i = recordStart; i >= recordStart - 21; i--){ + if(i - windowMin >= 0 && i <= windowMax && (i - START) % RESOLUTION == 0) { + Rtags[(i - windowMin) / RESOLUTION]++; + } + } + /*if(recordStart - windowMin >= 0 && recordStart <= windowMax && (recordStart - START) % RESOLUTION == 0) { + Rtags[(recordStart - windowMin) / RESOLUTION]++; + }*/ + } + else { + recordStart -= 10; + for(int i = recordStart; i <= recordStart + 21; i++){ + if(i - windowMin >= 0 && i <= windowMax && (i - START) % RESOLUTION == 0) { + Ftags[(i - windowMin) / RESOLUTION]++; + } + } + /*if(recordStart - windowMin >= 0 && recordStart <= windowMax && (recordStart - START) % RESOLUTION == 0) { + Ftags[(recordStart - windowMin) / RESOLUTION]++; + }*/ + } + } + iter.close(); + } catch (ArrayIndexOutOfBoundsException ex) { + System.out.println("Exception caught"); + } + Vector retVal = new Vector(); + retVal.add(Ftags); + retVal.add(Rtags); + return retVal; + } + + /** + * Resest the progressCounter and currentProgress to 0. + */ + public static void resetProgress() { + progressCounter = 0; + currentProgress = 0; + } +} \ No newline at end of file diff --git a/src/main/java/scriptmanager/objects/ArchTEx/CorrNode.java b/src/main/java/scriptmanager/objects/ArchTEx/CorrNode.java new file mode 100644 index 000000000..758378ef4 --- /dev/null +++ b/src/main/java/scriptmanager/objects/ArchTEx/CorrNode.java @@ -0,0 +1,131 @@ +package scriptmanager.objects.ArchTEx; + +import java.util.Vector; + +/** + * Object to help store ArchTEx Cross-Correlation analysis values. + *
+ * Code largely sourced from ArchTEx.analysis.corr.CorrNode in https://github.com/WilliamKMLai/ArchTEx + * + * @author William KM Lai + * @see scriptmanager.objects.ArchTEx.CorrExtract + * @see scriptmanager.scripts.BAM_Statistics.CrossCorrelation + */ +public class CorrNode { + private String uniqID = ""; + private String CHROM = ""; + private int BP_START = 0; + private int BP_STOP = 0; + private Vector tagPopulation; + + //Statistics + private double Sx = 0; + private double Sxx = 0; + private double Sy = 0; + private double Syy = 0; + private double[] Sxy = new double[1001]; + private double counter = 0; + + public CorrNode () { + + } + + public CorrNode (String chr, int start, int stop) { + CHROM = chr; + BP_START = start; + BP_STOP = stop; + } + + public void setCount(double count) { + counter = count; + } + + public void setSx(double newsx) { + Sx = newsx; + } + + public void setSxx(double newsxx) { + Sxx = newsxx; + } + + public void setSy(double newsy) { + Sy = newsy; + } + + public void setSyy(double newsyy) { + Syy = newsyy; + } + + public void setSxy(int index, double newsxy) { + Sxy[index] = newsxy; + } + + public void setSxy(double[] newsxy) { + Sxy = newsxy; + } + + public double getCount() { + return counter; + } + + public double getSx() { + return Sx; + } + + public double getSxx() { + return Sxx; + } + + public double getSy() { + return Sy; + } + + public double getSyy() { + return Syy; + } + + public double[] getSxy() { + return Sxy; + } + + public void setData(Vector tags) { + tagPopulation = tags; + } + + public Vector getData() { + return tagPopulation; + } + + public String getID() { + return uniqID; + } + + public void setID(String newid) { + uniqID = newid; + } + + public String getChrom() { + return CHROM; + } + + public void setChrom(String newchr) { + CHROM = newchr; + } + + public int getStop() { + return BP_STOP; + } + + public void setStop(int newbp) { + BP_STOP = newbp; + } + + public int getStart() { + return BP_START; + } + + public void setStart(int newbp) { + BP_START = newbp; + } +} \ No newline at end of file diff --git a/src/main/java/scriptmanager/objects/ArchTEx/CorrParameter.java b/src/main/java/scriptmanager/objects/ArchTEx/CorrParameter.java new file mode 100644 index 000000000..a69930801 --- /dev/null +++ b/src/main/java/scriptmanager/objects/ArchTEx/CorrParameter.java @@ -0,0 +1,166 @@ +package scriptmanager.objects.ArchTEx; + +//import java.io.File; +//import java.util.Vector; + +/** + * Object to help store ArchTEx Cross-Correlation parameter values. + *
+ * Code largely sourced from ArchTEx.analysis.corr.CorrParameter in https://github.com/WilliamKMLai/ArchTEx + * + * @author William KM Lai + * @see scriptmanager.scripts.BAM_Statistics.CrossCorrelation + * @see scriptmanager.window_interface.BAM_Statistics.CrossCorrelationOutput + * @see scriptmanager.window_interface.BAM_Statistics.CrossCorrelationWindow + */ +public class CorrParameter { +// //BAM Files and Accompanying indexes +// private Vector input; // Stores the names of input files +// private Vector index; // Stores the name of the index file + + //Parameter to determine between random sampling and complete sampling + private boolean corrType = false; //Default randomly sample genome + + //Random Sampling Parameters + private int windowSize = 50000; //Default set Window frame for each extraction to 50kb + private int iterations = 10; //Default set number of random iterations per chromosome + + //TODO if time add resolution and window frame variables + private int resolution = 1; //Default set correlation resolution + private int corrWindow = 1001; //Default set window size of correlation + + //Misc Parameters + private int threads = 1; //Default set number of cpu's to use to 1 +// private int currentExp = 0; //Default current experiment under analysis +// +// public CorrParameter() { +// input = new Vector(); +// index = new Vector(); +// } +// +// public void setExp(int newexp) { +// currentExp = newexp; +// } +// +// public int getExp() { +// return currentExp; +// } + + public boolean getCorrType() { + return corrType; + } + + public void setCorrType(boolean newtype) { + corrType = newtype; + } + +// public Vector getInput() { +// return input; +// } +// +// public void setInput(Vector newinput) { +// input = newinput; +// } +// +// public File getInputIndex(int index) { +// return input.get(index); +// } +// +// public void addInput(File newinput) { +// input.add(newinput); +// } +// +// public Vector getIndex() { +// return index; +// } +// +// public void setIndex(Vector newindex) { +// index = newindex; +// } +// +// public File getIndexIndex(int in) { +// return index.get(in); +// } +// +// public void addIndex(File newinput) { +// index.add(newinput); +// } + + public int getWindow() { + return windowSize; + } + + public void setWindow(int newwindow) { + windowSize = newwindow; + } + + public int getIterations() { + return iterations; + } + + public void setIterations(int newiter) { + iterations = newiter; + } + + public int getResolution() { + return resolution; + } + + public void setResolution(int newresolution) { + resolution = newresolution; + } + + public int getCorrWindow() { + return corrWindow; + } + + public void setCorrWindow(int newcorrWindow) { + corrWindow = newcorrWindow; + } + + public int getThreads() { + return threads; + } + + public void setThreads(int newthreads) { + threads = newthreads; + } + + public String printStats() { + String OUTPUT = ""; +// if(input == null) OUTPUT += "Input file: NONE\n"; +// else { +// for(int x = 0; x < input.size(); x++) { +// OUTPUT += "Input file: " + input.get(x) + "\n"; +// } +// } +// if(index == null) OUTPUT += "Index file: NONE\n"; +// else { +// for(int x = 0; x < index.size(); x++) { +// OUTPUT += "Index file: " + input.get(x) + "\n"; +// } +// } + OUTPUT += "Window Size: " + windowSize + "bp" + "\n"; + OUTPUT += "Iterations: " + iterations + "\n"; + OUTPUT += "Resolution: " + resolution + "bp" + "\n"; + OUTPUT += "CPU Cores: " + threads; + return OUTPUT; + } + +// public String[] getInputNames() { +// String[] names = new String[input.size()]; +// for (int x = 0; x < names.length; x++) { +// names[x] = input.get(x).getName(); +// } +// return names; +// } +// +// public String[] getIndexNames() { +// String[] names = new String[input.size()]; +// for (int x = 0; x < names.length; x++) { +// names[x] = input.get(x).getName(); +// } +// return names; +// } +} \ No newline at end of file diff --git a/src/main/java/scriptmanager/objects/LogItem.java b/src/main/java/scriptmanager/objects/LogItem.java new file mode 100644 index 000000000..d75b94cdb --- /dev/null +++ b/src/main/java/scriptmanager/objects/LogItem.java @@ -0,0 +1,83 @@ +package scriptmanager.objects; + +import java.sql.Timestamp; +import java.util.Date; + +/** + * Object to store a script execution in the unit of a single CLI call. + * + * @author Olivia Lang + * @see scriptmanager.main.LogManagerWindow + */ +public class LogItem { + + private Timestamp start; + private Timestamp stop = null; + private String command; + private int status = -1; // -1=incomplete, 0=complete, 1=error + + /** + * Initialize LogItem right before execution (sets start time to current instantiation time) + * + * @param cmd the string for cli execution (java -jar $SCRIPTMANAGER ...) + */ + public LogItem(String cmd) { + start = new Timestamp(new Date().getTime()); + command = cmd; + } + + /** + * Initialize LogItem post-execution + * + * @param cmd + * @param st + * @param sp + * @param s + */ + public LogItem(String cmd, Timestamp st, Timestamp sp, int s) { + command = cmd; + start = st; + stop = sp; + status = s; + } + + // Getters + public Timestamp getStartTime() { return start; } + public Timestamp getStopTime() { return stop; } + public String getCommand() { return command; } + public int getStatus() { return status; } + + public String getStatusString() { + if (status == -1) { + return("incomplete"); + } else if (status == 0) { + return("complete"); + } + return("error"); + } + + // Setters + public void setStartTime(Timestamp s) { start = s; } + public void setStopTime(Timestamp s) { stop = s; } + public void changeCommand(String cmd) { command = cmd; } + public void setStatus(int s) { status = s; } + + + /** + * Format LogItem to primitive String array for JTable compatibility as a row item. + * + * @return + */ + public String[] toStringArray() { + String[] row = new String[4]; + row[0] = getStatusString(); + row[1] = command.toString(); + row[2] = start.toString(); + row[3] = stop == null ? "N/A" : stop.toString(); + return(row); + } + + // Comparators -- for JTable sorts + // TODO: By start value + // TODO: By status +} diff --git a/src/main/java/scriptmanager/objects/PileupParameters.java b/src/main/java/scriptmanager/objects/PileupParameters.java index 8cdefb850..7fc314271 100644 --- a/src/main/java/scriptmanager/objects/PileupParameters.java +++ b/src/main/java/scriptmanager/objects/PileupParameters.java @@ -117,6 +117,9 @@ public class PileupParameters { private int CPU = 1; + /** + * Print all PileupParameters attributes (mainly for debugging purposes). + */ public void printAll(){ System.out.println( "<><><><><><><><><><><><><><><><><><><><>" ); System.out.println( "private File OUTPUT = " + OUTPUT ); @@ -510,26 +513,84 @@ public void setCPU(int cPU) { CPU = cPU; } + public String generateFileBase(String bed, String bam) { + String[] bedname = bed.split("\\."); + String[] bamname = bam.split("\\."); + + String read = "5read1"; + if (getAspect() == PileupParameters.FIVE && getRead() == PileupParameters.READ2) { + read = "5read2"; + } else if (getAspect() == PileupParameters.FIVE && getRead() == PileupParameters.ALLREADS) { + read = "5readc"; + } else if (getAspect() == PileupParameters.THREE && getRead() == PileupParameters.READ1) { + read = "3read1"; + } else if (getAspect() == PileupParameters.THREE && getRead() == PileupParameters.READ2) { + read = "3read2"; + } else if (getAspect() == PileupParameters.THREE && getRead() == PileupParameters.ALLREADS) { + read = "3readc"; + } else if (getAspect() == PileupParameters.MIDPOINT) { + read = "midpoint"; + } else if (getAspect() == PileupParameters.FRAGMENT) { + read = "fragment"; + } + + return (bedname[0] + "_" + bamname[0] + "_" + read); + } + + public String generateFileName(String bed, String bam, int strandnum) { + return (generateFileName(generateFileBase(bed, bam), strandnum)); + } + + public String generateFileName(String basename, int strandnum) { + String strand = "sense"; + if (strandnum == 1) { + strand = "anti"; + } else if (strandnum == 2) { + strand = "combined"; + } + + String filename = basename + "_" + strand; + if (getOutputType() == 1) { + filename += ".tab"; + } else { + filename += ".cdt"; + } + + if (getOutputGZIP()) { + filename += ".gz"; + } + + return filename; + } + /** - * recreate TagPileup command from the provided parameters (unused) + * Get the flag options for a cli execution with this object's parameter + * settings. * - * @return the string command + * @return the string of flags and options */ - public String getCLIcmd(){ + public String getCLIOptions(){ + String cliCommand = ""; + + // Add ASPECT + if (ASPECT == PileupParameters.FIVE ) { cliCommand += " -5"; } + else if (ASPECT == PileupParameters.THREE ) { cliCommand += " -3"; } + else if (ASPECT == PileupParameters.MIDPOINT ) { cliCommand += " -m"; } + else if (ASPECT == PileupParameters.FRAGMENT ) { cliCommand += " --full-fragment"; } + else { System.err.println("This should not print."); } - String cliCommand = "java -jar ScriptManager.jar read-analysis tag-pileup "; + // Add READ + if (READ == PileupParameters.READ1) { cliCommand += " -1"; } + else if (READ == PileupParameters.READ2) { cliCommand += " -2"; } + else if (READ == PileupParameters.ALLREADS) { cliCommand += " -a"; } + else { System.err.println("This should not print."); } - //Add READ - if(READ==0){ cliCommand += " -1"; } - else if(READ==1){ cliCommand += " -2"; } - else if(READ==2){ cliCommand += " -a"; } - else if(READ==3){ cliCommand += " -m"; } - else{ System.err.println("This should not print."); } - //Add STRAND - if(STRAND==0){ cliCommand += " --separate"; } - else if(STRAND==1){ cliCommand += " --combined"; } - else{ System.err.println("This should not print."); } - //Add TRANS + // Add STRAND + if (STRAND == 0) { cliCommand += " --separate"; } + else if (STRAND == 1) { cliCommand += " --combined"; } + else { System.err.println("This should not print."); } + + // Add TRANS if(TRANS==0){ cliCommand += " --no-smooth"; }else if(TRANS==1){ @@ -540,15 +601,17 @@ public String getCLIcmd(){ else{ cliCommand += " -G " + STDSIZE + " " + STDNUM; } }else{ System.err.println("This should not print."); } - //Add SHIFT - if(SHIFT!=0){ cliCommand += " -s " + SHIFT; } - //Add BIN - if(BIN!=1){ cliCommand += " -b " + BIN; } + // Add SHIFT + if (SHIFT != 0) { cliCommand += " -s " + SHIFT; } + // Add BIN + if (BIN != 1) { cliCommand += " -b " + BIN; } + // Add TagExtend + if (TAGEXTEND != 0) { cliCommand += " -e " + TAGEXTEND; } //Add STANDARD if(STANDARD==true){ cliCommand += " -t"; } //Add BLACKLIST - if(BLACKLIST!=null){ cliCommand += " -f " + BLACKLIST; } + if(BLACKLIST!=null){ cliCommand += " -f " + BLACKLIST.getAbsolutePath(); } //Add requirePE if(requirePE==true){ cliCommand += " -p"; } @@ -557,6 +620,7 @@ public String getCLIcmd(){ //Add MAX_INSERT if(MAX_INSERT!=-9999){ cliCommand += " -x " + MAX_INSERT; } + if (OUTTYPE == PileupParameters.TAB) { cliCommand += " --tab"; } //Add outputGZIP if(outputGZIP==true){ cliCommand += " -z"; } @@ -565,5 +629,4 @@ public String getCLIcmd(){ return(cliCommand); } - } diff --git a/src/main/java/scriptmanager/objects/ToolDescriptions.java b/src/main/java/scriptmanager/objects/ToolDescriptions.java index b6105eceb..a25de30fe 100644 --- a/src/main/java/scriptmanager/objects/ToolDescriptions.java +++ b/src/main/java/scriptmanager/objects/ToolDescriptions.java @@ -28,6 +28,7 @@ public class ToolDescriptions { public static final String se_stat_description = "Output BAM Header including alignment statistics and parameters given any indexed (BAI) BAM File."; public static final String pe_stat_description = "Generates Insert-size Histogram statistics (GEO requirement) and outputs BAM Header including alignment statistics and parameters given a sorted and indexed (BAI) paired-end BAM File."; public static final String bam_correlation_description = "Genome-Genome correlations for replicate comparisons given multiple sorted and indexed (BAI) BAM files."; + public static final String archtex_crosscorrelation_description = ("Calculate optimal tag shift based on ArchTEx implementation (PMID:22302569)"); // BAM Manipulation public static final String bam_indexer_description = "Generates BAI Index for input BAM files. Output BAI is in the same directory as input BAM file."; //* diff --git a/src/main/java/scriptmanager/scripts/BAM_Manipulation/BAIIndexer.java b/src/main/java/scriptmanager/scripts/BAM_Manipulation/BAIIndexer.java index f6603004a..03235c41a 100644 --- a/src/main/java/scriptmanager/scripts/BAM_Manipulation/BAIIndexer.java +++ b/src/main/java/scriptmanager/scripts/BAM_Manipulation/BAIIndexer.java @@ -1,47 +1,42 @@ package scriptmanager.scripts.BAM_Manipulation; -import htsjdk.samtools.BAMIndexer; -import htsjdk.samtools.SAMRecord; -import htsjdk.samtools.SamReader; -import htsjdk.samtools.SamReaderFactory; -import htsjdk.samtools.ValidationStringency; +import picard.sam.BuildBamIndex; import java.io.File; import java.io.IOException; -import java.text.NumberFormat; - -import javax.swing.JOptionPane; +import java.util.ArrayList; +/** + * Picard wrapper for BuildBamIndex + * + * @author Erik Pavloski + * @see scriptmanager.window_interface.BAM_Manipulation.BAIIndexerWIndow + */ public class BAIIndexer { + /** + * Index a BAM file and output said index to a file of the same name with a .bai + * extension + * + * @param input the BAM file to index + * @return the BAM index file (.bai) + * @throws IOException + */ public static File generateIndex(File input) throws IOException { - SamReaderFactory factory = SamReaderFactory.makeDefault().enable(SamReaderFactory.Option.INCLUDE_SOURCE_IN_RECORDS, SamReaderFactory.Option.CACHE_FILE_BASED_INDEXES, SamReaderFactory.Option.VALIDATE_CRC_CHECKSUMS).validationStringency(ValidationStringency.LENIENT); - File retVal = null; - System.out.println("Generating New Index File..."); - try{ - String output = input.getCanonicalPath() + ".bai"; - retVal = new File(output); - //Generate index - SamReader inputSam = factory.open(input); - BAMIndexer bamindex = new BAMIndexer(retVal, inputSam.getFileHeader()); - int counter = 0; - for(SAMRecord record : inputSam) { - if(counter % 1000000 == 0) { - System.out.print("Tags processed: " + NumberFormat.getIntegerInstance().format(counter) + "\r"); - System.out.flush(); - } - counter++; - bamindex.processAlignment(record); - } - bamindex.finish(); - inputSam.close(); - System.out.println("\nIndex File Generated"); - return retVal; - } - catch(htsjdk.samtools.SAMException exception){ - System.out.println(exception.getMessage()); - JOptionPane.showMessageDialog(null, exception.getMessage()); - retVal = null; - } + // Tells user that their file is being generated + System.out.println("Generating Index File..."); + // Build output filepath + String output = input.getCanonicalPath() + ".bai"; + File retVal = new File(output); + // Instatiate Picard object + final BuildBamIndex buildBamIndex = new BuildBamIndex(); + // Build input argument string + final ArrayList args = new ArrayList<>(); + args.add("INPUT=" + input.getAbsolutePath()); + args.add("OUTPUT=" + retVal.getAbsolutePath()); + // Call Picard with args + buildBamIndex.instanceMain(args.toArray(new String[args.size()])); + + System.out.println("Index File Generated"); return retVal; } } diff --git a/src/main/java/scriptmanager/scripts/BAM_Manipulation/BAMFileSort.java b/src/main/java/scriptmanager/scripts/BAM_Manipulation/BAMFileSort.java index 9bbf0f0d3..e91a28cde 100644 --- a/src/main/java/scriptmanager/scripts/BAM_Manipulation/BAMFileSort.java +++ b/src/main/java/scriptmanager/scripts/BAM_Manipulation/BAMFileSort.java @@ -1,25 +1,39 @@ package scriptmanager.scripts.BAM_Manipulation; +import htsjdk.samtools.SAMException; import htsjdk.samtools.SAMFileHeader; -import htsjdk.samtools.SAMFileWriter; -import htsjdk.samtools.SAMFileWriterFactory; -import htsjdk.samtools.SAMRecord; -import htsjdk.samtools.SamReader; -import htsjdk.samtools.SamReaderFactory; -import htsjdk.samtools.util.IOUtil; +import picard.sam.SortSam; import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +/** + * Picard wrapper for MergeSamFiles SortSam + * + * @author Erik Pavloski + * @see scriptmanager.window_interface.BAM_Manipulation.SortBAMWindow + */ public class BAMFileSort { - public static void sort(File INPUT, File OUTPUT) { - IOUtil.assertFileIsReadable(INPUT); - IOUtil.assertFileIsWritable(OUTPUT); - final SamReader reader = SamReaderFactory.makeDefault().open(INPUT); - reader.getFileHeader().setSortOrder(SAMFileHeader.SortOrder.coordinate); - final SAMFileWriter writer = new SAMFileWriterFactory().makeSAMOrBAMWriter(reader.getFileHeader(), false, OUTPUT); - for (final SAMRecord rec: reader) { - writer.addAlignment(rec); - } - writer.close(); - } -} + /** + * The following code uses Picard's SortSam to sort a BAM file by coordinate + * + * @param input the BAM file to be sorted (corresponds to INPUT) + * @param output the file to write the sorted BAM to (corresponds to OUTPUT) + * @throws SAMException + * @throws IOException + */ + public static void sort(File input, File output) throws SAMException, IOException { + // Tells user their File is being sorted + System.out.println("Sorting Bam File..."); + // Instatiate Picard object + final SortSam sorter = new SortSam(); + // Build input argument string + final ArrayList args = new ArrayList<>(); + args.add("INPUT=" + input.getAbsolutePath()); + args.add("OUTPUT=" + output.getAbsolutePath()); + args.add("SORT_ORDER=" + SAMFileHeader.SortOrder.coordinate); + // Call Picard with args + sorter.instanceMain(args.toArray(new String[args.size()])); + } +} \ No newline at end of file diff --git a/src/main/java/scriptmanager/scripts/BAM_Manipulation/MergeBAM.java b/src/main/java/scriptmanager/scripts/BAM_Manipulation/MergeBAM.java new file mode 100644 index 000000000..3548c4e23 --- /dev/null +++ b/src/main/java/scriptmanager/scripts/BAM_Manipulation/MergeBAM.java @@ -0,0 +1,49 @@ +package scriptmanager.scripts.BAM_Manipulation; + +import htsjdk.samtools.SAMException; +import picard.sam.MergeSamFiles; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; + +/** + * Picard wrapper for MergeSamFiles + * + * @author Erik Pavloski + * @see scriptmanager.window_interface.BAM_Manipulation + */ +public class MergeBAM { + /** + * Excecute the Picard MergeSamFiles command line tool after checking ever input file has been indexed. + * + * @param inputs the list of input files to merge (corresponds to several INPUT values) + * @param output the output file for the merged BAM file (corresponds to OUTPUT) + * @param useMultipleCpus whether or not to parallelize (corresponds to USE_THREADING) + * @throws SAMException + * @throws IOException + */ + public static void run(ArrayList inputs, File output, boolean useMultipleCpus) throws SAMException, IOException { + // Check all BAM files have an index + for (int x = 0; x args = new ArrayList<>(); + for (File input : inputs) { + args.add("INPUT=" + input.getAbsolutePath()); + } + args.add("OUTPUT=" + output.getAbsolutePath()); + if (useMultipleCpus) { + args.add("USE_THREADING=true"); + } + merger.instanceMain(args.toArray(new String[args.size()])); + System.out.println("BAM Files Merged."); + } +} \ No newline at end of file diff --git a/src/main/java/scriptmanager/scripts/BAM_Manipulation/MergeSamFiles.java b/src/main/java/scriptmanager/scripts/BAM_Manipulation/MergeSamFiles.java deleted file mode 100644 index d16f90945..000000000 --- a/src/main/java/scriptmanager/scripts/BAM_Manipulation/MergeSamFiles.java +++ /dev/null @@ -1,165 +0,0 @@ -package scriptmanager.scripts.BAM_Manipulation; - -/* - * The MIT License - * - * 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. - */ - -import htsjdk.samtools.MergingSamRecordIterator; -import htsjdk.samtools.SAMFileHeader; -import htsjdk.samtools.SAMFileWriter; -import htsjdk.samtools.SAMFileWriterFactory; -import htsjdk.samtools.SAMRecord; -import htsjdk.samtools.SAMSequenceDictionary; -import htsjdk.samtools.SamFileHeaderMerger; -import htsjdk.samtools.SamReader; -import htsjdk.samtools.SamReaderFactory; -import htsjdk.samtools.util.IOUtil; -import htsjdk.samtools.util.Log; -import htsjdk.samtools.util.ProgressLogger; -import picard.cmdline.CommandLineProgram; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -/** - * Reads a SAM or BAM file and combines the output to one file - * - * @author Tim Fennell - */ -public class MergeSamFiles extends CommandLineProgram { - private static final Log log = Log.getInstance(MergeSamFiles.class); - public List INPUT = new ArrayList(); - public File OUTPUT; - public SAMFileHeader.SortOrder SORT_ORDER = SAMFileHeader.SortOrder.coordinate; - public boolean ASSUME_SORTED = false; - public boolean MERGE_SEQUENCE_DICTIONARIES = false; - public boolean USE_THREADING = false; - public List COMMENT = new ArrayList(); - - private static final int PROGRESS_INTERVAL = 1000000; - - public MergeSamFiles(List in, File out) { - INPUT = in; - OUTPUT = out; - } - - public MergeSamFiles(List in, File out, boolean thread) { - INPUT = in; - OUTPUT = out; - USE_THREADING = thread; - } - - public int run() { - doWork(); - return 0; - } - - /** Combines multiple SAM/BAM files into one. */ - protected int doWork() { - boolean matchedSortOrders = true; - // ASSUME_SORTED = true; - - // Open the files for reading and writing - final List readers = new ArrayList(); - final List headers = new ArrayList(); - { - SAMSequenceDictionary dict = null; // Used to try and reduce redundant SDs in memory - - for (final File inFile : INPUT) { - IOUtil.assertFileIsReadable(inFile); - final SamReader in = SamReaderFactory.makeDefault().referenceSequence(REFERENCE_SEQUENCE).open(inFile); - readers.add(in); - headers.add(in.getFileHeader()); - - // A slightly hackish attempt to keep memory consumption down when merging - // multiple files with - // large sequence dictionaries (10,000s of sequences). If the dictionaries are - // identical, then - // replace the duplicate copies with a single dictionary to reduce the memory - // footprint. - if (dict == null) { - dict = in.getFileHeader().getSequenceDictionary(); - } else if (dict.equals(in.getFileHeader().getSequenceDictionary())) { - in.getFileHeader().setSequenceDictionary(dict); - } - matchedSortOrders = matchedSortOrders && in.getFileHeader().getSortOrder() == SORT_ORDER; - } - } - - // If all the input sort orders match the output sort order then just merge them - // and - // write on the fly, otherwise setup to merge and sort before writing out the - // final file - IOUtil.assertFileIsWritable(OUTPUT); - final boolean presorted; - final SAMFileHeader.SortOrder headerMergerSortOrder; - final boolean mergingSamRecordIteratorAssumeSorted; - - if (matchedSortOrders || SORT_ORDER == SAMFileHeader.SortOrder.unsorted || ASSUME_SORTED) { - log.info("Input files are in same order as output so sorting to temp directory is not needed."); - headerMergerSortOrder = SORT_ORDER; - mergingSamRecordIteratorAssumeSorted = ASSUME_SORTED; - presorted = true; - } else { - log.info("Sorting input files using temp directory " + TMP_DIR); - headerMergerSortOrder = SAMFileHeader.SortOrder.unsorted; - mergingSamRecordIteratorAssumeSorted = false; - presorted = false; - } - - final SamFileHeaderMerger headerMerger = new SamFileHeaderMerger(headerMergerSortOrder, headers, - MERGE_SEQUENCE_DICTIONARIES); - final MergingSamRecordIterator iterator = new MergingSamRecordIterator(headerMerger, readers, - mergingSamRecordIteratorAssumeSorted); - final SAMFileHeader header = headerMerger.getMergedHeader(); - for (final String comment : COMMENT) { - header.addComment(comment); - } - - // Unique to this build - for (final File inFile : INPUT) { - header.addComment("@CO\tReplicate:" + inFile.getName()); - } - - header.setSortOrder(SORT_ORDER); - final SAMFileWriterFactory samFileWriterFactory = new SAMFileWriterFactory(); - if (USE_THREADING) { - samFileWriterFactory.setUseAsyncIo(true); - } - final SAMFileWriter out = samFileWriterFactory.makeSAMOrBAMWriter(header, presorted, OUTPUT); - - // Lastly loop through and write out the records - final ProgressLogger progress = new ProgressLogger(log, PROGRESS_INTERVAL); - while (iterator.hasNext()) { - final SAMRecord record = iterator.next(); - out.addAlignment(record); - progress.record(record); - } - - log.info("Finished reading inputs."); - iterator.close(); - out.close(); - return 0; - } -} \ No newline at end of file diff --git a/src/main/java/scriptmanager/scripts/BAM_Statistics/CrossCorrelation.java b/src/main/java/scriptmanager/scripts/BAM_Statistics/CrossCorrelation.java new file mode 100644 index 000000000..340012f0d --- /dev/null +++ b/src/main/java/scriptmanager/scripts/BAM_Statistics/CrossCorrelation.java @@ -0,0 +1,225 @@ +package scriptmanager.scripts.BAM_Statistics; + +import java.awt.Component; +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; +import java.sql.Timestamp; +import java.util.Date; +import java.util.Vector; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import htsjdk.samtools.AbstractBAMFileIndex; +import htsjdk.samtools.SAMSequenceRecord; +import htsjdk.samtools.SamReader; +import htsjdk.samtools.SamReaderFactory; +import htsjdk.samtools.ValidationStringency; +import scriptmanager.charts.CompositePlot; +import scriptmanager.objects.ArchTEx.CorrExtract; +import scriptmanager.objects.ArchTEx.CorrNode; +import scriptmanager.objects.ArchTEx.CorrParameter; + +/** + * Class with static method for performing ArchTEx's cross-correlation analysis. + *
+ * Code largely sourced from ArchTEx.analysis.corr.CorrLoad in https://github.com/WilliamKMLai/ArchTEx + * + * @author William KM Lai + * @see scriptmanager.objects.ArchTEx.CorrParameter + * @see scriptmanager.cli.BAM_Statistics.CrossCorrelationCLI + * @see scriptmanager.window_interface.BAM_Statistics.CrossCorrelationOutput + * @see scriptmanager.window_interface.BAM_Statistics.CrossCorrelationWindow + */ +public class CrossCorrelation { + + /** + * Perform the cross-correlation analysis from ArchTEx of a BAM file by + * correlating forward and reverse strand pileups at various tag shifts to + * determine the tag shift with the strongest correlation between strands. + * + * @param outfilepath the output file to write TagShift-->Correlation score + * pair results + * @param bamFile the BAM file to determine the best strand correlated tag + * shift for + * @param param the object for storing user-defined parameters for the + * cross-correlation + * @param PS_CCDATA where progress updates and raw correlation scores are + * written as the script executes + * @return the JFreeChart-based line plot of corrlation (y-axis) scores for a + * range of shifts (x-axis) + */ + public static Component correlate(File outfilepath, File bamFile, CorrParameter param, PrintStream PS_CCDATA){ + //Check if BAI index file exists + File f = new File(bamFile + ".bai"); + if(!f.exists() || f.isDirectory()) { + if( PS_CCDATA!=null ){ PS_CCDATA.println("BAI Index File does not exist for: " + bamFile.getName()); } + System.err.println("BAI Index File does not exist for: " + bamFile.getName()); + return(null); + } + + // Output files to be saved + PrintStream OUT_CCDATA = null; + + // Set output file printstream + if( outfilepath!=null ) { + try { + OUT_CCDATA = new PrintStream(outfilepath); + } + catch (IOException ioe) { ioe.printStackTrace(); } + } + + // Start timestamp + String time = new Timestamp(new Date().getTime()).toString(); + System.out.println("Start: " + time); + if (PS_CCDATA!=null ) { PS_CCDATA.println("Start: " + time); } + + // Print params to output + String paramDescription = "# type=Genome;"; + if (!param.getCorrType()) { + paramDescription = "# type=Random; window=" + param.getCorrWindow() + "; numSites=" + param.getIterations() + ";"; + } + System.out.println(paramDescription); + if (PS_CCDATA!=null ) { PS_CCDATA.println(paramDescription); } + if (OUT_CCDATA!=null ) { OUT_CCDATA.println(paramDescription); } + + // Data for building C-C Plots and printing C-C Data + double[] xData = new double[param.getCorrWindow()]; + double[] yData = new double[param.getCorrWindow()]; + + //Code to get individual chromosome stats + final SamReaderFactory factory = SamReaderFactory.makeDefault().enable(SamReaderFactory.Option.INCLUDE_SOURCE_IN_RECORDS, SamReaderFactory.Option.VALIDATE_CRC_CHECKSUMS).validationStringency(ValidationStringency.SILENT); + SamReader reader = factory.open(bamFile); + AbstractBAMFileIndex bai = (AbstractBAMFileIndex) reader.indexing().getIndex(); +// SAMFileReader reader = new SAMFileReader(bamFile, new File(bamFile.getAbsoluteFile()+".bai")); +// AbstractBAMFileIndex bai = (AbstractBAMFileIndex) reader.getIndex(); + + double[] Sx = new double[param.getCorrWindow()]; + double[] Sxx = new double[param.getCorrWindow()]; + double[] Sy = new double[param.getCorrWindow()]; + double[] Syy = new double[param.getCorrWindow()]; + double[] Sxy = new double[param.getCorrWindow()]; + double[] count = new double[param.getCorrWindow()]; + + //Need to extract all at once for optimal efficiency + Vector ChromosomeWindows; + + for(int numchrom = 0; numchrom < bai.getNumberOfReferences(); numchrom++) { + //Object to keep track of the chromosomal data + ChromosomeWindows = new Vector(); + SAMSequenceRecord seq = reader.getFileHeader().getSequence(numchrom); +// net.sf.samtools.SAMSequenceRecord seq = reader.getFileHeader().getSequence(numchrom); + System.out.println("\nAnalyzing: " + seq.getSequenceName()); + if (PS_CCDATA!=null ) { PS_CCDATA.println("Analyzing: " + seq.getSequenceName()); } + if(!param.getCorrType()) { + //Randomly select #_Iteration sample from each chromosome at user-specified size + for(int x = 0; x < param.getIterations(); x++) { + int start = (int)(Math.random() * (double)(seq.getSequenceLength() - param.getWindow())); + int stop = start + param.getWindow(); + ChromosomeWindows.add(new CorrNode(seq.getSequenceName(), start, stop)); + } + + } else { + //Break chromosome into 100kb chunks and assign to independent BLASTNodes + int numwindows = (int) (seq.getSequenceLength() / 100000); + int Resolution = param.getResolution(); // Resolution controls increment + int windowSize = param.getCorrWindow(); // Size of theoretical sliding window + for(int x = 0; x < numwindows; x++) { + int start = x * 100000; + int stop = start + 100000 + windowSize; + ChromosomeWindows.add(new CorrNode(seq.getSequenceName(), start, stop)); + } + int finalstart = numwindows * 100000; + int finalstop = (seq.getSequenceLength() / Resolution) * Resolution; + ChromosomeWindows.add(new CorrNode(seq.getSequenceName(), finalstart, finalstop)); + } + + //Load Chromosome Windows with data from ALL experiments + int numberofThreads = param.getThreads(); + int nodeSize = ChromosomeWindows.size(); + if(nodeSize < numberofThreads) { + numberofThreads = nodeSize; + } + ExecutorService parseMaster = Executors.newFixedThreadPool(numberofThreads); + int subset = 0; + int currentindex = 0; + for(int x = 0; x < numberofThreads; x++) { + currentindex += subset; + if(numberofThreads == 1) subset = nodeSize; + else if(nodeSize % numberofThreads == 0) subset = nodeSize / numberofThreads; + else { + int remainder = nodeSize % numberofThreads; + if(x < remainder ) subset = (int)(((double)nodeSize / (double)numberofThreads) + 1); + else subset = (int)(((double)nodeSize / (double)numberofThreads)); + } + + CorrExtract nodeextract = new CorrExtract(bamFile, param, ChromosomeWindows, currentindex, subset); + parseMaster.execute(nodeextract); + } + parseMaster.shutdown(); + while (!parseMaster.isTerminated()) { + } + + CorrExtract.resetProgress(); + + for(int x = 0; x < ChromosomeWindows.size(); x++) { + for(int y = 0; y < Sx.length; y++) { + Sx[y] += ChromosomeWindows.get(x).getSx(); + Sxx[y] += ChromosomeWindows.get(x).getSxx(); + Sy[y] += ChromosomeWindows.get(x).getSy(); + Syy[y] += ChromosomeWindows.get(x).getSyy(); + Sxy[y] += ChromosomeWindows.get(x).getSxy()[y]; + count[y] += ChromosomeWindows.get(x).getCount(); + } + } + } + + System.err.println("\nTagShift\tCorrelation"); + if (PS_CCDATA!=null ) { PS_CCDATA.println("\nTagShift\tCorrelation"); } + if (OUT_CCDATA!=null ) { OUT_CCDATA.println("TagShift\tCorrelation"); } + + double[] numerator = new double[param.getCorrWindow()]; + double[] denominator = new double[param.getCorrWindow()]; + + double PEAK = -999; + int PEAK_SHIFT = -999; + for(int x = 0; x < Sx.length; x++) { + numerator[x] = Sxy[x] - ((Sx[x] * Sy[x]) / count[x]); + denominator[x] = Math.sqrt((Sxx[x] - ((Sx[x] * Sx[x]) / count[x])) * (Syy[x] - ((Sy[x] * Sy[x] / count[x])))); + yData[x] = numerator[x] / denominator[x]; + xData[x] = x; + if(yData[x] > PEAK) { + PEAK = yData[x]; + PEAK_SHIFT = (int)x; + } + //System.out.println(Sx[x] + "\t" + Sxx[x] + "\t" + Sy[x] + "\t" + Syy[x] + "\t" + Sxy[x] + "\t" + count[x]); + if (PS_CCDATA!=null ) { PS_CCDATA.println((int)xData[x] + "\t" + yData[x]); } + if (OUT_CCDATA!=null ) { OUT_CCDATA.println((int)xData[x] + "\t" + yData[x]); } + } + + // Close BAM & BAI files + try{ + reader.close(); + bai.close(); + }catch (IOException ioe) { ioe.printStackTrace(); } + + System.err.println("Analysis Complete\n"); + if (PS_CCDATA!=null ) { PS_CCDATA.println("Analysis Complete\n"); } + + System.out.println("Peak At: " + PEAK_SHIFT + "\nScore: " + PEAK); + if (PS_CCDATA!=null ) { PS_CCDATA.println("Peak At: " + PEAK_SHIFT + "\nScore: " + PEAK); } + if (OUT_CCDATA!=null ) { OUT_CCDATA.println("Peak At: " + PEAK_SHIFT + "\nScore: " + PEAK); } + + // Close output stream + if(PS_CCDATA != null){ PS_CCDATA.close(); } + if(OUT_CCDATA != null){ OUT_CCDATA.close(); } + + // Finish timestamp + time = new Timestamp(new Date().getTime()).toString(); + System.out.println("Finish: " + time); + if (PS_CCDATA!=null ) { PS_CCDATA.println("Finish: " + time); } + + return(CompositePlot.createCompositePlot(xData, yData, bamFile.getName())); + } +} diff --git a/src/main/java/scriptmanager/scripts/Read_Analysis/TagPileup.java b/src/main/java/scriptmanager/scripts/Read_Analysis/TagPileup.java index a83c7b1d1..ba3cf3f45 100644 --- a/src/main/java/scriptmanager/scripts/Read_Analysis/TagPileup.java +++ b/src/main/java/scriptmanager/scripts/Read_Analysis/TagPileup.java @@ -64,11 +64,11 @@ public void run() throws IOException { String NAME0; String NAME1; if (outMatrixBasename == null) { - NAME0 = PARAM.getOutputDirectory() + File.separator + generateFileName(BED.getName(), BAM.getName(), 0); - NAME1 = PARAM.getOutputDirectory() + File.separator + generateFileName(BED.getName(), BAM.getName(), 1); + NAME0 = PARAM.getOutputDirectory() + File.separator + PARAM.generateFileName(BED.getName(), BAM.getName(), 0); + NAME1 = PARAM.getOutputDirectory() + File.separator + PARAM.generateFileName(BED.getName(), BAM.getName(), 1); } else { - NAME0 = generateFileName(outMatrixBasename, 0); - NAME1 = generateFileName(outMatrixBasename, 1); + NAME0 = PARAM.generateFileName(outMatrixBasename, 0); + NAME1 = PARAM.generateFileName(outMatrixBasename, 1); } // Build streams if (PARAM.getOutputGZIP()) { @@ -82,9 +82,9 @@ public void run() throws IOException { // Set FileName String NAME2; if (outMatrixBasename == null) { - NAME2 = PARAM.getOutputDirectory() + File.separator + generateFileName(BED.getName(), BAM.getName(), 2); + NAME2 = PARAM.getOutputDirectory() + File.separator + PARAM.generateFileName(BED.getName(), BAM.getName(), 2); } else { - NAME2 = generateFileName(outMatrixBasename, 2); + NAME2 = PARAM.generateFileName(outMatrixBasename, 2); } if (PARAM.getOutputGZIP()) { OUT_S1 = new OutputStreamWriter(new GZIPOutputStream(new FileOutputStream(NAME2)), "UTF-8"); @@ -251,18 +251,18 @@ public void run() throws IOException { } COMPOSITE.println(); if (STRAND == 0) { - COMPOSITE.print(generateFileName(BED.getName(), BAM.getName(), 0)); + COMPOSITE.print(PARAM.generateFileName(BED.getName(), BAM.getName(), 0)); for (int a = 0; a < AVG_S1.length; a++) { COMPOSITE.print("\t" + AVG_S1[a]); } COMPOSITE.println(); - COMPOSITE.print(generateFileName(BED.getName(), BAM.getName(), 1)); + COMPOSITE.print(PARAM.generateFileName(BED.getName(), BAM.getName(), 1)); for (int a = 0; a < AVG_S2.length; a++) { COMPOSITE.print("\t" + AVG_S2[a]); } COMPOSITE.println(); } else { - COMPOSITE.print(generateFileName(BED.getName(), BAM.getName(), 2)); + COMPOSITE.print(PARAM.generateFileName(BED.getName(), BAM.getName(), 2)); for (int a = 0; a < AVG_S1.length; a++) { COMPOSITE.print("\t" + AVG_S1[a]); } @@ -323,52 +323,6 @@ public Vector validateBED(Vector COORD, File BAM) throws IOE return FINAL; } - public String generateFileName(String bed, String bam, int strandnum) { - String[] bedname = bed.split("\\."); - String[] bamname = bam.split("\\."); - - String read = "5read1"; - if (PARAM.getAspect() == PileupParameters.FIVE && PARAM.getRead() == PileupParameters.READ2) { - read = "5read2"; - } else if (PARAM.getAspect() == PileupParameters.FIVE && PARAM.getRead() == PileupParameters.ALLREADS) { - read = "5readc"; - } else if (PARAM.getAspect() == PileupParameters.THREE && PARAM.getRead() == PileupParameters.READ1) { - read = "3read1"; - } else if (PARAM.getAspect() == PileupParameters.THREE && PARAM.getRead() == PileupParameters.READ2) { - read = "3read2"; - } else if (PARAM.getAspect() == PileupParameters.THREE && PARAM.getRead() == PileupParameters.ALLREADS) { - read = "3readc"; - } else if (PARAM.getAspect() == PileupParameters.MIDPOINT) { - read = "midpoint"; - } else if (PARAM.getAspect() == PileupParameters.FRAGMENT) { - read = "fragment"; - } - - return (generateFileName(bedname[0] + "_" + bamname[0] + "_" + read, strandnum)); - } - - public String generateFileName(String basename, int strandnum) { - String strand = "sense"; - if (strandnum == 1) { - strand = "anti"; - } else if (strandnum == 2) { - strand = "combined"; - } - - String filename = basename + "_" + strand; - if (PARAM.getOutputType() == 1) { - filename += ".tab"; - } else { - filename += ".cdt"; - } - - if (PARAM.getOutputGZIP()) { - filename += ".gz"; - } - - return filename; - } - private static String getTimeStamp() { Date date = new Date(); String time = new Timestamp(date.getTime()).toString(); diff --git a/src/main/java/scriptmanager/window_interface/BAM_Manipulation/BAIIndexerWindow.java b/src/main/java/scriptmanager/window_interface/BAM_Manipulation/BAIIndexerWindow.java index 1e1481523..27dee2d46 100644 --- a/src/main/java/scriptmanager/window_interface/BAM_Manipulation/BAIIndexerWindow.java +++ b/src/main/java/scriptmanager/window_interface/BAM_Manipulation/BAIIndexerWindow.java @@ -1,5 +1,7 @@ package scriptmanager.window_interface.BAM_Manipulation; +import htsjdk.samtools.SAMException; + import java.awt.Component; import java.awt.Container; import java.awt.Cursor; @@ -47,13 +49,19 @@ class Task extends SwingWorker { @Override public Void doInBackground() throws IOException { setProgress(0); - for(int x = 0; x < BAMFiles.size(); x++) { - BAIIndexer.generateIndex(BAMFiles.get(x)); - int percentComplete = (int)(((double)(x + 1) / BAMFiles.size()) * 100); - setProgress(percentComplete); - } - setProgress(100); - JOptionPane.showMessageDialog(null, "Indexing Complete"); + try { + for(int x = 0; x < BAMFiles.size(); x++) { + // Execute Picard wrapper + BAIIndexer.generateIndex(BAMFiles.get(x)); + // Update progress + int percentComplete = (int)(((double)(x + 1) / BAMFiles.size()) * 100); + setProgress(percentComplete); + } + setProgress(100); + JOptionPane.showMessageDialog(null, "Indexing Complete"); + } catch (SAMException se) { + JOptionPane.showMessageDialog(null, se.getMessage()); + } return null; } @@ -158,6 +166,3 @@ public void massXable(Container con, boolean status) { } } } - - - diff --git a/src/main/java/scriptmanager/window_interface/BAM_Manipulation/BAMMarkDupWindow.java b/src/main/java/scriptmanager/window_interface/BAM_Manipulation/BAMMarkDupWindow.java index 3b6019a46..8cdd3df69 100644 --- a/src/main/java/scriptmanager/window_interface/BAM_Manipulation/BAMMarkDupWindow.java +++ b/src/main/java/scriptmanager/window_interface/BAM_Manipulation/BAMMarkDupWindow.java @@ -12,7 +12,9 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; +import java.sql.Timestamp; import java.util.ArrayList; +import java.util.Date; import java.util.List; import javax.swing.DefaultListModel; @@ -31,9 +33,11 @@ import javax.swing.SwingWorker; import javax.swing.border.EmptyBorder; +import scriptmanager.objects.LogItem; import scriptmanager.util.FileSelection; -import scriptmanager.scripts.BAM_Manipulation.BAMMarkDuplicates; import scriptmanager.scripts.BAM_Manipulation.BAIIndexer; +import scriptmanager.cli.BAM_Manipulation.BAMRemoveDupCLI; +import scriptmanager.scripts.BAM_Manipulation.BAMMarkDuplicates; @SuppressWarnings("serial") public class BAMMarkDupWindow extends JFrame implements ActionListener, PropertyChangeListener { @@ -60,11 +64,13 @@ class Task extends SwingWorker { @Override public Void doInBackground() throws Exception { setProgress(0); + LogItem old_li = new LogItem(""); try { for(int x = 0; x < BAMFiles.size(); x++) { String[] NAME = BAMFiles.get(x).getName().split("\\."); File OUTPUT = null; File METRICS = null; + if(OUTPUT_PATH != null) { OUTPUT = new File(OUTPUT_PATH.getCanonicalPath() + File.separator + NAME[0] + "_dedup.bam"); METRICS = new File(OUTPUT_PATH.getCanonicalPath() + File.separator + NAME[0] + "_dedup.metrics"); @@ -72,17 +78,34 @@ public Void doInBackground() throws Exception { OUTPUT = new File(NAME[0] + "_dedup.bam"); METRICS = new File(NAME[0] + "_dedup.metrics"); } + + + // Initialize LogItem + String command = BAMRemoveDupCLI.getCLIcommand(BAMFiles.get(x), chckbxRemoveDuplicates.isSelected(), OUTPUT, METRICS); + LogItem new_li = new LogItem(command); + firePropertyChange("log", old_li, new_li); + + // Run script BAMMarkDuplicates.mark(BAMFiles.get(x), chckbxRemoveDuplicates.isSelected(), OUTPUT, METRICS); - if(chckbxGenerateBaiIndex.isSelected()) { BAIIndexer.generateIndex(OUTPUT); } + // Update log item + new_li.setStopTime(new Timestamp(new Date().getTime())); + new_li.setStatus(0); + old_li = new_li; + // Index output if selected + if(chckbxGenerateBaiIndex.isSelected()) { BAIIndexer.generateIndex(OUTPUT); } + + // Update progress int percentComplete = (int)(((double)(x + 1) / BAMFiles.size()) * 100); setProgress(percentComplete); } } catch (SAMException se){ JOptionPane.showMessageDialog(null, se.getMessage()); } + firePropertyChange("log", old_li, null); setProgress(100); + JOptionPane.showMessageDialog(null, "Mark Duplicates Complete"); return null; } @@ -216,6 +239,9 @@ public void propertyChange(PropertyChangeEvent evt) { if ("progress" == evt.getPropertyName()) { int progress = (Integer) evt.getNewValue(); progressBar.setValue(progress); + } else if ("log" == evt.getPropertyName()) { + firePropertyChange("log", evt.getOldValue(), evt.getNewValue()); + } } diff --git a/src/main/java/scriptmanager/window_interface/BAM_Manipulation/MergeBAMWindow.java b/src/main/java/scriptmanager/window_interface/BAM_Manipulation/MergeBAMWindow.java index a5d58ad1e..3d20bdf4f 100644 --- a/src/main/java/scriptmanager/window_interface/BAM_Manipulation/MergeBAMWindow.java +++ b/src/main/java/scriptmanager/window_interface/BAM_Manipulation/MergeBAMWindow.java @@ -1,5 +1,7 @@ package scriptmanager.window_interface.BAM_Manipulation; +import htsjdk.samtools.SAMException; + import java.awt.Component; import java.awt.Container; import java.awt.Cursor; @@ -10,7 +12,6 @@ import java.beans.PropertyChangeListener; import java.io.File; import java.util.ArrayList; -import java.util.List; import javax.swing.DefaultListModel; import javax.swing.JButton; @@ -31,7 +32,7 @@ import scriptmanager.util.FileSelection; import scriptmanager.scripts.BAM_Manipulation.BAIIndexer; -import scriptmanager.scripts.BAM_Manipulation.MergeSamFiles; +import scriptmanager.scripts.BAM_Manipulation.MergeBAM; @SuppressWarnings("serial") public class MergeBAMWindow extends JFrame implements ActionListener, PropertyChangeListener { @@ -39,9 +40,9 @@ public class MergeBAMWindow extends JFrame implements ActionListener, PropertyCh protected JFileChooser fc = new JFileChooser(new File(System.getProperty("user.dir"))); final DefaultListModel expList; - List BAMFiles = new ArrayList(); + ArrayList BAMFiles = new ArrayList(); private File OUTPUT = null; - private File OUTPUT_PATH = null; + private File OUT_DIR = null; private JButton btnLoad; private JButton btnRemoveBam; @@ -59,15 +60,21 @@ class Task extends SwingWorker { @Override public Void doInBackground() throws Exception { setProgress(0); - if(OUTPUT_PATH != null) { OUTPUT = new File(OUTPUT_PATH.getCanonicalPath() + File.separator + txtOutput.getText()); } - else { OUTPUT = new File(txtOutput.getText()); } - MergeSamFiles merge = new MergeSamFiles(BAMFiles, OUTPUT, chckbxUseMultipleCpus.isSelected()); - merge.run(); - - if(chckbxGenerateBaiindex.isSelected()) { - BAIIndexer.generateIndex(OUTPUT); - } - JOptionPane.showMessageDialog(null, "Merging Complete"); + try { + // Build output filepath + if(OUT_DIR != null) { OUTPUT = new File(OUT_DIR.getCanonicalPath() + File.separator + txtOutput.getText()); } + else { OUTPUT = new File(txtOutput.getText()); } + // Execute Picard wrapper + MergeBAM.run(BAMFiles, OUTPUT, chckbxUseMultipleCpus.isSelected()); + // Index if checkbox selected + if(chckbxGenerateBaiindex.isSelected()) { + BAIIndexer.generateIndex(OUTPUT); + } + setProgress(100); + JOptionPane.showMessageDialog(null, "Merging Complete"); + } catch (SAMException se) { + JOptionPane.showMessageDialog(null, se.getMessage()); + } return null; } @@ -187,9 +194,10 @@ public void actionPerformed(ActionEvent arg0) { sl_contentPane.putConstraint(SpringLayout.EAST, btnOutput, -150, SpringLayout.EAST, contentPane); btnOutput.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { - OUTPUT_PATH = FileSelection.getOutputDir(fc); - if(OUTPUT_PATH != null) { - lblDefaultToLocal.setText(OUTPUT_PATH.getAbsolutePath()); + File temp = FileSelection.getOutputDir(fc); + if(temp != null) { + OUT_DIR = temp; + lblDefaultToLocal.setText(OUT_DIR.getAbsolutePath()); } } }); diff --git a/src/main/java/scriptmanager/window_interface/BAM_Manipulation/SortBAMWindow.java b/src/main/java/scriptmanager/window_interface/BAM_Manipulation/SortBAMWindow.java index 97362eb81..2cf424940 100644 --- a/src/main/java/scriptmanager/window_interface/BAM_Manipulation/SortBAMWindow.java +++ b/src/main/java/scriptmanager/window_interface/BAM_Manipulation/SortBAMWindow.java @@ -1,5 +1,7 @@ package scriptmanager.window_interface.BAM_Manipulation; +import htsjdk.samtools.SAMException; + import java.awt.Cursor; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -35,9 +37,9 @@ public class SortBAMWindow extends JFrame implements ActionListener, PropertyChangeListener { private JPanel contentPane; protected JFileChooser fc = new JFileChooser(new File(System.getProperty("user.dir"))); - + final DefaultListModel expList; - private File OUTPUT_PATH = null; + private File OUT_DIR = null; List BAMFiles = new ArrayList(); private JButton btnLoad; @@ -54,17 +56,25 @@ class Task extends SwingWorker { @Override public Void doInBackground() throws Exception { setProgress(0); - for(int x = 0; x < BAMFiles.size(); x++) { - String[] NAME = BAMFiles.get(x).getName().split("\\."); - File OUTPUT = null; - if(OUTPUT_PATH != null) { OUTPUT = new File(OUTPUT_PATH.getCanonicalPath() + File.separator + NAME[0] + "_sorted.bam"); } - else { OUTPUT = new File(NAME[0] + "_sorted.bam"); } - BAMFileSort.sort(BAMFiles.get(x), OUTPUT); - int percentComplete = (int)(((double)(x + 1) / BAMFiles.size()) * 100); - setProgress(percentComplete); - } - setProgress(100); - JOptionPane.showMessageDialog(null, "Sorting Complete"); + try { + for(int x = 0; x < BAMFiles.size(); x++) { + // Build output filepath + String[] NAME = BAMFiles.get(x).getName().split("\\."); + File OUTPUT = null; + if(OUT_DIR != null) { OUTPUT = new File(OUT_DIR.getCanonicalPath() + File.separator + NAME[0] + "_sorted.bam"); } + else { OUTPUT = new File(NAME[0] + "_sorted.bam"); } + // Execute Picard wrapper + BAMFileSort.sort(BAMFiles.get(x), OUTPUT); + // Update progress + int percentComplete = (int)(((double)(x + 1) / BAMFiles.size()) * 100); + setProgress(percentComplete); + + } + setProgress(100); + JOptionPane.showMessageDialog(null, "Sorting Complete"); + } catch (SAMException se) { + JOptionPane.showMessageDialog(null, se.getMessage()); + } return null; } @@ -144,9 +154,10 @@ public void actionPerformed(ActionEvent arg0) { btnOutput = new JButton("Output Directory"); btnOutput.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { - OUTPUT_PATH = FileSelection.getOutputDir(fc); - if(OUTPUT_PATH != null) { - lblDefaultToLocal.setText(OUTPUT_PATH.getAbsolutePath()); + File temp = FileSelection.getOutputDir(fc); + if(temp != null) { + OUT_DIR = temp; + lblDefaultToLocal.setText(OUT_DIR.getAbsolutePath()); } } }); diff --git a/src/main/java/scriptmanager/window_interface/BAM_Statistics/CrossCorrelationOutput.java b/src/main/java/scriptmanager/window_interface/BAM_Statistics/CrossCorrelationOutput.java new file mode 100644 index 000000000..7b7c515a8 --- /dev/null +++ b/src/main/java/scriptmanager/window_interface/BAM_Statistics/CrossCorrelationOutput.java @@ -0,0 +1,139 @@ +package scriptmanager.window_interface.BAM_Statistics; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintStream; +import java.util.Vector; + +import javax.swing.JFrame; +import javax.swing.JLayeredPane; +import javax.swing.JOptionPane; +import javax.swing.JScrollPane; +import javax.swing.JTabbedPane; +import javax.swing.JTextArea; +import javax.swing.SpringLayout; + +import scriptmanager.objects.CustomOutputStream; +import scriptmanager.objects.ArchTEx.CorrParameter; +import scriptmanager.scripts.BAM_Statistics.CrossCorrelation; +import scriptmanager.util.ExtensionFileFilter; + +/** + * Graphical window for running the CrossCorrelation script and + * displaying the Tag Shift-to-Correlation plots, the raw Tag + * Shift-to-Correlation values, and script progress. + * + * @author William KM Lai + * @see scriptmanager.objects.ArchTEx.CorrParameter + * @see scriptmanager.scripts.BAM_Statistics.CrossCorrelation + * @see scriptmanager.window_interface.BAM_Statistics.CrossCorrelationWindow + */ +@SuppressWarnings("serial") +public class CrossCorrelationOutput extends JFrame { + + Vector bamFiles = null; + private File OUT_DIR = null; + private boolean OUTPUT_STATUS = false; + private CorrParameter PARAM; + + final JLayeredPane layeredPane; + final JTabbedPane tabbedPane; + final JTabbedPane tabbedPane_CCPlots; + final JTabbedPane tabbedPane_CCData; + + /** + * Initialize tab frames for scrollable JTextArea and charts as well as save + * inputs for calling the script. + * + * @param o the output directory to write output to + * @param input the list of input BAM files to process + * @param param the custom parameter storing object for running the ArchTEx script + * @param ostats whether to output results to a file or not + */ + public CrossCorrelationOutput(File o, Vector input, CorrParameter param, boolean ostats) { + setTitle("BAM File Cross Correlation Plots and Statistics"); + setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + setBounds(150, 150, 800, 600); + + layeredPane = new JLayeredPane(); + getContentPane().add(layeredPane, BorderLayout.CENTER); + SpringLayout sl_layeredPane = new SpringLayout(); + layeredPane.setLayout(sl_layeredPane); + + tabbedPane = new JTabbedPane(JTabbedPane.TOP); + sl_layeredPane.putConstraint(SpringLayout.NORTH, tabbedPane, 6, SpringLayout.NORTH, layeredPane); + sl_layeredPane.putConstraint(SpringLayout.WEST, tabbedPane, 6, SpringLayout.WEST, layeredPane); + sl_layeredPane.putConstraint(SpringLayout.SOUTH, tabbedPane, -6, SpringLayout.SOUTH, layeredPane); + sl_layeredPane.putConstraint(SpringLayout.EAST, tabbedPane, -6, SpringLayout.EAST, layeredPane); + layeredPane.add(tabbedPane); + + bamFiles = input; + OUT_DIR = o; + OUTPUT_STATUS = ostats; + PARAM = param; + + tabbedPane_CCPlots = new JTabbedPane(JTabbedPane.TOP); + tabbedPane_CCData = new JTabbedPane(JTabbedPane.TOP); + tabbedPane.addTab("C-C Plots", null, tabbedPane_CCPlots, null); + tabbedPane.addTab("C-C Data", null, tabbedPane_CCData, null); + } + + /** + * Call script, validate BAI files, build output basename, and display progress + * with results by instantiating results tabs for each input BAM file. + * + * @throws IOException + */ + public void run() throws IOException { + // Check if BAI index file exists for all BAM files + boolean[] BAMvalid = new boolean[bamFiles.size()]; + for (int z = 0; z < bamFiles.size(); z++) { + File BAM = bamFiles.get(z); // Pull current BAM file + File f = new File(BAM + ".bai"); // Generate file name for BAI index file + if (!f.exists() || f.isDirectory()) { + BAMvalid[z] = false; + System.err.println("BAI Index File does not exist for: " + BAM.getName()); + JOptionPane.showMessageDialog(null, "BAI Index File does not exist for: " + BAM.getName()); + } else { + BAMvalid[z] = true; + } + } + //Iterate through all BAM files in Vector + for(int x = 0; x < bamFiles.size(); x++) { + System.out.println("Cross-Correlation: " + bamFiles.get(x).getName()); + + if (BAMvalid[x]) { + // Construct Basename + File OUT_FILEPATH = null; + if(OUTPUT_STATUS){ + try{ + String NAME = ExtensionFileFilter.stripExtension(bamFiles.get(x).getName()) + "_CrossCorrelation.txt"; + if(OUT_DIR == null) { OUT_FILEPATH = new File(NAME); } + else { OUT_FILEPATH = new File( OUT_DIR.getCanonicalPath() + File.separator + NAME); } + } + catch (FileNotFoundException e) { e.printStackTrace(); } + } + + // Initialize PrintStream and TextArea for C-C Data + PrintStream ps_ccdata = null; + JTextArea CC_DATA = new JTextArea(); + CC_DATA.setCaretPosition(0); + CC_DATA.setLineWrap(false); + CC_DATA.setEditable(false); + ps_ccdata = new PrintStream(new CustomOutputStream( CC_DATA )); + tabbedPane_CCData.add(bamFiles.get(x).getName(), new JScrollPane(CC_DATA, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED)); + + //Call public static method from scripts + Component chart = CrossCorrelation.correlate(OUT_FILEPATH, bamFiles.get(x), PARAM, ps_ccdata); + tabbedPane_CCPlots.add(bamFiles.get(x).getName(), chart); + + ps_ccdata.close(); + firePropertyChange("bam",x, x + 1); + } + } + } + +} diff --git a/src/main/java/scriptmanager/window_interface/BAM_Statistics/CrossCorrelationWindow.java b/src/main/java/scriptmanager/window_interface/BAM_Statistics/CrossCorrelationWindow.java new file mode 100644 index 000000000..9bfd5a8a2 --- /dev/null +++ b/src/main/java/scriptmanager/window_interface/BAM_Statistics/CrossCorrelationWindow.java @@ -0,0 +1,416 @@ +package scriptmanager.window_interface.BAM_Statistics; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.File; +import java.io.IOException; +import java.util.Vector; + +import javax.swing.BorderFactory; +import javax.swing.ButtonGroup; +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.JRadioButton; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.ListSelectionModel; +import javax.swing.SpringLayout; +import javax.swing.SwingConstants; +import javax.swing.SwingWorker; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +import scriptmanager.objects.ArchTEx.CorrParameter; +import scriptmanager.util.ExtensionFileFilter; +import scriptmanager.util.FileSelection; + +/** + * Graphical window for user argument selection and execution of the + * CrossCorrelation script.
+ * Code largely sourced from ArchTEx.components.CorrelationParametersWindow in + * https://github.com/WilliamKMLai/ArchTEx + * + * @author William KM Lai + * @see scriptmanager.objects.ArchTEx.CorrParameter + * @see scriptmanager.scripts.BAM_Statistics.CrossCorrelation + * @see scriptmanager.window_interface.BAM_Statistics.CrossCorrelationOutput + */ +@SuppressWarnings("serial") +public class CrossCorrelationWindow extends JFrame implements ActionListener, PropertyChangeListener { + private JPanel contentPane; + protected JFileChooser fc = new JFileChooser(new File(System.getProperty("user.dir"))); + private JCheckBox chckbxOutputStatistics; + private JButton btnLoad; + private JButton btnRemoveBam; + private JButton btnOutputDirectory; + private JButton btnCorrelate; + private JTextField txtCPU; + private JTextField txtWind; + private JTextField txtSample; +// private JLabel lblCurrentOutput; + private JLabel lblDefaultToLocal; + + private JPanel pnlSamplingParams; + + private JRadioButton rdbtnRandom; + private JRadioButton rdbtnGenome; + + final DefaultListModel expList; + Vector BAMFiles = new Vector(); + private File OUT_DIR = new File(System.getProperty("user.dir")); + + private JProgressBar progressBar; + public Task task; + + /** + * Organize user inputs for calling script. + */ + class Task extends SwingWorker { + @Override + public Void doInBackground() { + setProgress(0); + try { + if(Integer.parseInt(txtCPU.getText()) < 1) { + JOptionPane.showMessageDialog(null, "Invalid number of CPUs!!! Must be integer greater than 0"); + } else if (BAMFiles.size() < 1) { + JOptionPane.showMessageDialog(null, "Must load at least one BAM file"); + } else if(rdbtnRandom.isSelected() && Integer.parseInt(txtWind.getText()) < 1) { + JOptionPane.showMessageDialog(null, "Invalid window size!!! Must be integer greater than 0"); + } else if(rdbtnRandom.isSelected() && Integer.parseInt(txtSample.getText()) < 1) { + JOptionPane.showMessageDialog(null, "Invalid number of samples!!! Must be greater than 0"); + } else { + // Load Parameters + System.out.println("Loading Parameters..."); + CorrParameter param = new CorrParameter(); + if(rdbtnRandom.isSelected()) { + param.setCorrType(false); + param.setWindow(Integer.parseInt(txtWind.getText())); + param.setIterations(Integer.parseInt(txtSample.getText())); + param.setThreads(Integer.parseInt(txtCPU.getText())); + } else if(rdbtnGenome.isSelected()) { + param.setCorrType(true); + } + System.out.println("Parameters Loaded.\n"); + // Initialize output window and run + CrossCorrelationOutput script_obj = new CrossCorrelationOutput(OUT_DIR, BAMFiles, param, chckbxOutputStatistics.isSelected()); + script_obj.addPropertyChangeListener("bam", new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent propertyChangeEvent) { + int temp = (Integer) propertyChangeEvent.getNewValue(); + int percentComplete = (int)(((double)(temp) / BAMFiles.size()) * 100); + setProgress(percentComplete); + } + }); + script_obj.setVisible(true); + script_obj.run(); + } + } catch(NumberFormatException nfe){ + JOptionPane.showMessageDialog(null, "Input Fields Must Contain Integers"); + } catch (IOException ioe) { + ioe.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + setProgress(100); + return null; + } + + public void done() { + massXable(contentPane, true); + setCursor(null); //turn off the wait cursor + } + } + + /** + * Instantiate window with graphical interface design. + */ + public CrossCorrelationWindow() { + setTitle("BAM Cross Correlation"); + setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + + setBounds(125, 125, 490, 420); + contentPane = new JPanel(); + contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); + setContentPane(contentPane); + SpringLayout sl_contentPane = new SpringLayout(); + contentPane.setLayout(sl_contentPane); + setMinimumSize(new Dimension(490, 380)); + + JScrollPane scrollPane_BAM = new JScrollPane(); + sl_contentPane.putConstraint(SpringLayout.SOUTH, scrollPane_BAM, -290, SpringLayout.SOUTH, contentPane); + sl_contentPane.putConstraint(SpringLayout.WEST, scrollPane_BAM, 5, SpringLayout.WEST, contentPane); + sl_contentPane.putConstraint(SpringLayout.EAST, scrollPane_BAM, -5, SpringLayout.EAST, contentPane); + contentPane.add(scrollPane_BAM); + + expList = new DefaultListModel(); + final JList listExp = new JList(expList); + listExp.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + scrollPane_BAM.setViewportView(listExp); + + btnLoad = new JButton("Load BAM Files"); + sl_contentPane.putConstraint(SpringLayout.NORTH, scrollPane_BAM, 6, SpringLayout.SOUTH, btnLoad); + sl_contentPane.putConstraint(SpringLayout.WEST, btnLoad, 5, SpringLayout.WEST, contentPane); + btnLoad.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + File[] newBAMFiles = FileSelection.getFiles(fc,"bam"); + if(newBAMFiles != null) { + for(int x = 0; x < newBAMFiles.length; x++) { + BAMFiles.add(newBAMFiles[x]); + expList.addElement(newBAMFiles[x].getName()); + } + } + } + }); + contentPane.add(btnLoad); + + btnRemoveBam = new JButton("Remove BAM"); + sl_contentPane.putConstraint(SpringLayout.NORTH, btnRemoveBam, 0, SpringLayout.NORTH, contentPane); + sl_contentPane.putConstraint(SpringLayout.EAST, btnRemoveBam, -5, SpringLayout.EAST, contentPane); + sl_contentPane.putConstraint(SpringLayout.NORTH, btnLoad, 0, SpringLayout.NORTH, btnRemoveBam); + btnRemoveBam.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent arg0) { + while(listExp.getSelectedIndex() > -1) { + BAMFiles.remove(listExp.getSelectedIndex()); + expList.remove(listExp.getSelectedIndex()); + } + } + }); + contentPane.add(btnRemoveBam); + + // Execution and progress bar components + btnCorrelate = new JButton("Correlate"); + sl_contentPane.putConstraint(SpringLayout.SOUTH, btnCorrelate, -5, SpringLayout.SOUTH, contentPane); + sl_contentPane.putConstraint(SpringLayout.WEST, btnCorrelate, 170, SpringLayout.WEST, contentPane); + sl_contentPane.putConstraint(SpringLayout.EAST, btnCorrelate, -170, SpringLayout.EAST, contentPane); + contentPane.add(btnCorrelate); + + progressBar = new JProgressBar(); + sl_contentPane.putConstraint(SpringLayout.EAST, progressBar, -5, SpringLayout.EAST, contentPane); + sl_contentPane.putConstraint(SpringLayout.NORTH, btnCorrelate, -3, SpringLayout.NORTH, progressBar); + sl_contentPane.putConstraint(SpringLayout.EAST, btnCorrelate, -18, SpringLayout.WEST, progressBar); + sl_contentPane.putConstraint(SpringLayout.SOUTH, progressBar, -10, SpringLayout.SOUTH, contentPane); + progressBar.setStringPainted(true); + contentPane.add(progressBar); + + // Correlation param components + JPanel pnlCorrelationParams = new JPanel(); + sl_contentPane.putConstraint(SpringLayout.NORTH, pnlCorrelationParams, 10, SpringLayout.SOUTH, scrollPane_BAM); + sl_contentPane.putConstraint(SpringLayout.WEST, pnlCorrelationParams, 0, SpringLayout.WEST, scrollPane_BAM); + sl_contentPane.putConstraint(SpringLayout.EAST, pnlCorrelationParams, 0, SpringLayout.EAST, scrollPane_BAM); + sl_contentPane.putConstraint(SpringLayout.SOUTH, pnlCorrelationParams, 90, SpringLayout.SOUTH, scrollPane_BAM); + contentPane.add(pnlCorrelationParams); + + SpringLayout sl_CorrelationParams = new SpringLayout(); + pnlCorrelationParams.setLayout(sl_CorrelationParams); + TitledBorder ttlCorrelationParams = BorderFactory.createTitledBorder("Correlation Parameters"); + ttlCorrelationParams.setTitleFont(new Font("Lucida Grande", Font.ITALIC, 13)); + pnlCorrelationParams.setBorder(ttlCorrelationParams); + +// JLabel lblCorrelationParameters = new JLabel("Correlation Parameters"); +// lblCorrelationParameters.setFont(new Font("Tahoma", Font.BOLD, 11)); +// lblCorrelationParameters.setBounds(185, 315, 133, 14); +// contentPane.add(lblCorrelationParameters); + + //Radio Buttons to Control Whole-Genome vs Random Sampling + JLabel lblCorrelationType = new JLabel("Correlation Type:"); + sl_CorrelationParams.putConstraint(SpringLayout.NORTH, lblCorrelationType, 10, SpringLayout.NORTH, pnlCorrelationParams); + sl_CorrelationParams.putConstraint(SpringLayout.WEST, lblCorrelationType, 10, SpringLayout.WEST, pnlCorrelationParams); + pnlCorrelationParams.add(lblCorrelationType); + + rdbtnGenome = new JRadioButton("Whole Genome"); + sl_CorrelationParams.putConstraint(SpringLayout.NORTH, rdbtnGenome, -2, SpringLayout.NORTH, lblCorrelationType); + sl_CorrelationParams.putConstraint(SpringLayout.WEST, rdbtnGenome, 10, SpringLayout.EAST, lblCorrelationType); + rdbtnGenome.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent arg0) { + pnlSamplingParams.setEnabled(false); + for (Component c : pnlSamplingParams.getComponents()) { + c.setEnabled(false); + } + } + }); + pnlCorrelationParams.add(rdbtnGenome); + + rdbtnRandom = new JRadioButton("Random Sampling"); + sl_CorrelationParams.putConstraint(SpringLayout.NORTH, rdbtnRandom, -2, SpringLayout.NORTH, lblCorrelationType); + sl_CorrelationParams.putConstraint(SpringLayout.WEST, rdbtnRandom, 10, SpringLayout.EAST, rdbtnGenome); + rdbtnRandom.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent arg0) { + pnlSamplingParams.setEnabled(true); + for (Component c : pnlSamplingParams.getComponents()) { + c.setEnabled(true); + } + } + }); + pnlCorrelationParams.add(rdbtnRandom); + + ButtonGroup groupCorrType = new ButtonGroup(); + groupCorrType.add(rdbtnGenome); + groupCorrType.add(rdbtnRandom); + rdbtnGenome.setSelected(true); + + JLabel lblCpusToUse = new JLabel("CPU's to Use:"); + sl_CorrelationParams.putConstraint(SpringLayout.NORTH, lblCpusToUse, 10, SpringLayout.SOUTH, lblCorrelationType); + sl_CorrelationParams.putConstraint(SpringLayout.WEST, lblCpusToUse, 10, SpringLayout.WEST, pnlCorrelationParams); + pnlCorrelationParams.add(lblCpusToUse); + + txtCPU = new JTextField("1"); + sl_CorrelationParams.putConstraint(SpringLayout.NORTH, txtCPU, 0, SpringLayout.NORTH, lblCpusToUse); + sl_CorrelationParams.putConstraint(SpringLayout.WEST, txtCPU, 10, SpringLayout.EAST, lblCpusToUse); + txtCPU.setHorizontalAlignment(SwingConstants.CENTER); + txtCPU.setColumns(10); + pnlCorrelationParams.add(txtCPU); + + // Random Sampling param components (FlowLayout) + pnlSamplingParams = new JPanel(); + sl_contentPane.putConstraint(SpringLayout.NORTH, pnlSamplingParams, 10, SpringLayout.SOUTH, pnlCorrelationParams); + sl_contentPane.putConstraint(SpringLayout.WEST, pnlSamplingParams, 0, SpringLayout.WEST, scrollPane_BAM); + sl_contentPane.putConstraint(SpringLayout.EAST, pnlSamplingParams, 0, SpringLayout.EAST, scrollPane_BAM); + sl_contentPane.putConstraint(SpringLayout.SOUTH, pnlSamplingParams, 60, SpringLayout.SOUTH, pnlCorrelationParams); + contentPane.add(pnlSamplingParams); + + TitledBorder ttlSamplingParams = BorderFactory.createTitledBorder("Random Sampling Parameters"); + ttlSamplingParams.setTitleFont(new Font("Lucida Grande", Font.ITALIC, 13)); + pnlSamplingParams.setBorder(ttlSamplingParams); + + JLabel lblWindowSizebp = new JLabel("Window Size (bp):"); + pnlSamplingParams.add(lblWindowSizebp); + + txtWind = new JTextField("50000"); + txtWind.setHorizontalAlignment(SwingConstants.LEFT); + txtWind.setHorizontalAlignment(SwingConstants.CENTER); + txtWind.setColumns(10); + pnlSamplingParams.add(txtWind); + + JLabel lblTagSamplings = new JLabel("# of Samplings"); + pnlSamplingParams.add(lblTagSamplings); + + txtSample = new JTextField("10"); + txtSample.setHorizontalAlignment(SwingConstants.CENTER); + txtSample.setColumns(10); + pnlSamplingParams.add(txtSample); + + // Output Parameters + JPanel pnlOutputOptions = new JPanel(); + sl_contentPane.putConstraint(SpringLayout.NORTH, pnlOutputOptions, 10, SpringLayout.SOUTH, pnlSamplingParams); + sl_contentPane.putConstraint(SpringLayout.WEST, pnlOutputOptions, 0, SpringLayout.WEST, scrollPane_BAM); + sl_contentPane.putConstraint(SpringLayout.EAST, pnlOutputOptions, 0, SpringLayout.EAST, scrollPane_BAM); + sl_contentPane.putConstraint(SpringLayout.SOUTH, pnlOutputOptions, -10, SpringLayout.NORTH, btnCorrelate); + contentPane.add(pnlOutputOptions); + + SpringLayout sl_OutputOptions = new SpringLayout(); + pnlOutputOptions.setLayout(sl_OutputOptions); + TitledBorder ttlOutputOptions = BorderFactory.createTitledBorder("Output Options"); + ttlOutputOptions.setTitleFont(new Font("Lucida Grande", Font.ITALIC, 13)); + pnlOutputOptions.setBorder(ttlOutputOptions); + + chckbxOutputStatistics = new JCheckBox("Output Statistics"); + sl_OutputOptions.putConstraint(SpringLayout.NORTH, chckbxOutputStatistics, 6, SpringLayout.NORTH, pnlOutputOptions); + sl_OutputOptions.putConstraint(SpringLayout.WEST, chckbxOutputStatistics, 0, SpringLayout.WEST, pnlOutputOptions); + pnlOutputOptions.add(chckbxOutputStatistics); + + chckbxOutputStatistics.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + Component[] clist = {btnOutputDirectory, lblDefaultToLocal}; + if (chckbxOutputStatistics.isSelected()) { + for (Component c : clist) { + c.setEnabled(true); + } + } else { + for (Component c : clist) { + c.setEnabled(false); + } + } + } + }); + + btnOutputDirectory = new JButton("Output Directory"); + sl_OutputOptions.putConstraint(SpringLayout.NORTH, btnOutputDirectory, 6, SpringLayout.SOUTH, chckbxOutputStatistics); + sl_OutputOptions.putConstraint(SpringLayout.WEST, btnOutputDirectory, 6, SpringLayout.WEST, pnlOutputOptions); + btnOutputDirectory.setEnabled(false); + pnlOutputOptions.add(btnOutputDirectory); + + btnOutputDirectory.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + OUT_DIR = FileSelection.getOutputDir(fc); + if (OUT_DIR != null) { + lblDefaultToLocal.setText(OUT_DIR.getAbsolutePath()); + try { + lblDefaultToLocal.setText("..." + ExtensionFileFilter.getSubstringEnd(OUT_DIR, 43)); + } catch (IOException e1) { + System.err.println("Output directory may not be loaded!"); + e1.printStackTrace(); + } + } else { + OUT_DIR = new File(System.getProperty("user.dir")); + lblDefaultToLocal.setText("Default to Local Directory"); + } + lblDefaultToLocal.setToolTipText(OUT_DIR.getAbsolutePath()); + } + }); + + lblDefaultToLocal = new JLabel("Default to Local Directory"); + sl_OutputOptions.putConstraint(SpringLayout.NORTH, lblDefaultToLocal, 2, SpringLayout.NORTH, btnOutputDirectory); + sl_OutputOptions.putConstraint(SpringLayout.WEST, lblDefaultToLocal, 6, SpringLayout.EAST, btnOutputDirectory); + lblDefaultToLocal.setFont(new Font("Dialog", Font.PLAIN, 12)); + lblDefaultToLocal.setBackground(Color.WHITE); + lblDefaultToLocal.setToolTipText("Directory path"); + lblDefaultToLocal.setEnabled(false); + pnlOutputOptions.add(lblDefaultToLocal); + + // Disable sampling parameters for initialize state + pnlSamplingParams.setEnabled(false); + for (Component c : pnlSamplingParams.getComponents()) { + c.setEnabled(false); + } + + btnCorrelate.addActionListener(this); + } + + @Override + public void actionPerformed(ActionEvent arg0) { + massXable(contentPane, false); + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + + task = new Task(); + task.addPropertyChangeListener(this); + task.execute(); + } + + /** + * Invoked when task's progress property changes. + */ + @Override + public void propertyChange(PropertyChangeEvent evt) { + if ("progress" == evt.getPropertyName()) { + int progress = (Integer) evt.getNewValue(); + progressBar.setValue(progress); + } + } + + public void massXable(Container con, boolean status) { + for(Component c : con.getComponents()) { + c.setEnabled(status); + if (c instanceof Container) { + massXable((Container) c, status); + } + } + } +} diff --git a/src/main/java/scriptmanager/window_interface/Coordinate_Manipulation/BED_Manipulation/BEDtoGFFWindow.java b/src/main/java/scriptmanager/window_interface/Coordinate_Manipulation/BED_Manipulation/BEDtoGFFWindow.java index 34d7fa29d..f3484bfb0 100644 --- a/src/main/java/scriptmanager/window_interface/Coordinate_Manipulation/BED_Manipulation/BEDtoGFFWindow.java +++ b/src/main/java/scriptmanager/window_interface/Coordinate_Manipulation/BED_Manipulation/BEDtoGFFWindow.java @@ -11,6 +11,8 @@ import java.beans.PropertyChangeListener; import java.io.File; import java.io.IOException; +import java.sql.Timestamp; +import java.util.Date; import java.util.Vector; import javax.swing.DefaultListModel; @@ -29,8 +31,10 @@ import javax.swing.SwingWorker; import javax.swing.border.EmptyBorder; +import scriptmanager.objects.LogItem; import scriptmanager.util.ExtensionFileFilter; import scriptmanager.util.FileSelection; +import scriptmanager.cli.Coordinate_Manipulation.BED_Manipulation.BEDtoGFFCLI; import scriptmanager.scripts.Coordinate_Manipulation.BED_Manipulation.BEDtoGFF; /** @@ -67,6 +71,7 @@ class Task extends SwingWorker { @Override public Void doInBackground() throws IOException { setProgress(0); + LogItem old_li = new LogItem(""); for (int x = 0; x < BEDFiles.size(); x++) { File XBED = BEDFiles.get(x); // Set outfilepath @@ -75,11 +80,21 @@ public Void doInBackground() throws IOException { OUTPUT = OUT_DIR + File.separator + OUTPUT; } OUTPUT += chckbxGzipOutput.isSelected() ? ".gz" : ""; + // Initialize LogItem + String command = BEDtoGFFCLI.getCLIcommand(new File(OUTPUT), XBED, chckbxGzipOutput.isSelected()); + LogItem new_li = new LogItem(command); + firePropertyChange("log", old_li, new_li); // Execute conversion and update progress BEDtoGFF.convertBEDtoGFF(new File(OUTPUT), XBED, chckbxGzipOutput.isSelected()); + // Update log item + new_li.setStopTime(new Timestamp(new Date().getTime())); + new_li.setStatus(0); + old_li = new_li; + // Update progress int percentComplete = (int) (((double) (x + 1) / BEDFiles.size()) * 100); setProgress(percentComplete); } + firePropertyChange("log", old_li, null); setProgress(100); JOptionPane.showMessageDialog(null, "Conversion Complete"); return null; @@ -186,7 +201,7 @@ public void actionPerformed(ActionEvent e) { sl_contentPane.putConstraint(SpringLayout.EAST, btnOutput, -150, SpringLayout.EAST, contentPane); contentPane.add(btnOutput); btnConvert.addActionListener(this); - + chckbxGzipOutput = new JCheckBox("Output GZIP"); sl_contentPane.putConstraint(SpringLayout.NORTH, chckbxGzipOutput, 0, SpringLayout.NORTH, btnOutput); sl_contentPane.putConstraint(SpringLayout.EAST, chckbxGzipOutput, -10, SpringLayout.EAST, contentPane); @@ -210,6 +225,8 @@ public void propertyChange(PropertyChangeEvent evt) { if ("progress" == evt.getPropertyName()) { int progress = (Integer) evt.getNewValue(); progressBar.setValue(progress); + } else if ("log" == evt.getPropertyName()) { + firePropertyChange("log", evt.getOldValue(), evt.getNewValue()); } } diff --git a/src/main/java/scriptmanager/window_interface/Read_Analysis/TagPileupOutput.java b/src/main/java/scriptmanager/window_interface/Read_Analysis/TagPileupOutput.java index fce63eef3..adc477359 100644 --- a/src/main/java/scriptmanager/window_interface/Read_Analysis/TagPileupOutput.java +++ b/src/main/java/scriptmanager/window_interface/Read_Analysis/TagPileupOutput.java @@ -5,7 +5,9 @@ import java.io.File; import java.io.IOException; import java.io.PrintStream; +import java.sql.Timestamp; import java.util.ArrayList; +import java.util.Date; import java.util.Vector; import javax.swing.JFrame; @@ -19,6 +21,8 @@ import scriptmanager.charts.CompositePlot; import scriptmanager.objects.PileupParameters; import scriptmanager.objects.CustomOutputStream; +import scriptmanager.objects.LogItem; +import scriptmanager.cli.Read_Analysis.TagPileupCLI; import scriptmanager.scripts.Read_Analysis.TagPileup; import scriptmanager.util.BAMUtilities; @@ -44,18 +48,14 @@ public class TagPileupOutput extends JFrame { final JTabbedPane tabbedPane_Statistics; /** - * Store inputs and initialize a tabbed pane to display composite plot - * results and the composite plot values. + * Store inputs and initialize a tabbed pane to display composite plot results + * and the composite plot values. * - * @param be - * the list of input BED coordinate RefPT files - * @param ba - * the list of input BAM tag alignment files - * @param param - * the custom object to store configurations for how to perform - * the TagPileup - * @param colors - * the list of colors to use for the composite plots + * @param be the list of input BED coordinate RefPT files + * @param ba the list of input BAM tag alignment files + * @param param the custom object to store configurations for how to perform + * the TagPileup + * @param colors the list of colors to use for the composite plots */ public TagPileupOutput(Vector be, Vector ba, PileupParameters param, ArrayList colors) { setTitle("Tag Pileup Composite"); @@ -112,6 +112,7 @@ public void run() throws IOException { } } + LogItem old_li = new LogItem(""); int PROGRESS = 0; for (int z = 0; z < BAMFiles.size(); z++) { File BAM = BAMFiles.get(z); // Pull current BAM file @@ -123,15 +124,28 @@ public void run() throws IOException { PARAM.setRatio(BAMUtilities.calculateStandardizationRatio(BAM, PARAM.getRead())); } + // Loop through each BED file for (int BED_Index = 0; BED_Index < BEDFiles.size(); BED_Index++) { - System.err.println( "Processing BAM: " + BAM.getName() + "\tCoordinate: " + BEDFiles.get(BED_Index).getName()); + File XBED = BEDFiles.get(BED_Index); + System.err.println( "Processing BAM: " + BAM.getName() + "\tCoordinate: " + XBED.getName()); - JTextArea STATS = new JTextArea(); // Generate statistics object + // Generate statistics object for printing composite results + JTextArea STATS = new JTextArea(); STATS.setEditable(false); // Make it un-editable PrintStream ps = new PrintStream(new CustomOutputStream(STATS)); - TagPileup script_obj = new TagPileup(BEDFiles.get(BED_Index), BAM, PARAM, ps, null); + // Initialize LogItem + String command = TagPileupCLI.getCLIcommand(XBED, BAM, PARAM); + LogItem new_li = new LogItem(command); + firePropertyChange("log", old_li, new_li); + + // Execute conversion and update progress + TagPileup script_obj = new TagPileup(XBED, BAM, PARAM, ps, null); script_obj.run(); + // Update log item + new_li.setStopTime(new Timestamp(new Date().getTime())); + new_li.setStatus(0); + old_li = new_li; // Make composite plots if (PARAM.getStrand() == PileupParameters.SEPARATE) { @@ -140,15 +154,18 @@ public void run() throws IOException { tabbedPane_Scatterplot.add(BAM.getName(), CompositePlot.createCompositePlot(script_obj.DOMAIN, script_obj.AVG_S1, BEDFiles.get(BED_Index).getName(), COLORS)); } + // Add statistics to new tab STATS.setCaretPosition(0); - JScrollPane newpane = new JScrollPane(STATS, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, - JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + JScrollPane newpane = new JScrollPane(STATS, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); tabbedPane_Statistics.add(BAM.getName(), newpane); + + // Update progress firePropertyChange("tag", PROGRESS, PROGRESS + 1); PROGRESS++; } } } + firePropertyChange("log", old_li, null); } } diff --git a/src/main/java/scriptmanager/window_interface/Read_Analysis/TagPileupWindow.java b/src/main/java/scriptmanager/window_interface/Read_Analysis/TagPileupWindow.java index e43d03387..eb25de206 100644 --- a/src/main/java/scriptmanager/window_interface/Read_Analysis/TagPileupWindow.java +++ b/src/main/java/scriptmanager/window_interface/Read_Analysis/TagPileupWindow.java @@ -46,6 +46,7 @@ import javax.swing.border.EmptyBorder; import javax.swing.border.TitledBorder; +import htsjdk.samtools.SAMException; import scriptmanager.objects.CompositeCartoon; import scriptmanager.objects.PileupParameters; import scriptmanager.objects.ReadFragmentCartoon; @@ -223,8 +224,8 @@ public Void doInBackground() throws IOException, InterruptedException { //debug gui by printing params // param.printAll(); + // Initialize, addPropertyChangeListeners, and execute TagPileupOutput pile = new TagPileupOutput(BEDFiles, BAMFiles, param, colors); - pile.addPropertyChangeListener("tag", new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent propertyChangeEvent) { int temp = (Integer) propertyChangeEvent.getNewValue(); @@ -232,8 +233,11 @@ public void propertyChange(PropertyChangeEvent propertyChangeEvent) { setProgress(percentComplete); } }); - - + pile.addPropertyChangeListener("log", new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent evt) { + firePropertyChange("log", evt.getOldValue(), evt.getNewValue()); + } + }); pile.setVisible(true); pile.run(); @@ -242,6 +246,8 @@ public void propertyChange(PropertyChangeEvent propertyChangeEvent) { } } catch (NumberFormatException nfe) { JOptionPane.showMessageDialog(null, "Invalid Input in Fields!!!", "Validate Input", JOptionPane.ERROR_MESSAGE); + } catch (SAMException se) { + JOptionPane.showMessageDialog(null, se.getMessage(), "Validate Input", JOptionPane.ERROR_MESSAGE); } return null; } @@ -1134,6 +1140,8 @@ public void propertyChange(PropertyChangeEvent evt) { if ("progress" == evt.getPropertyName()) { int progress = (Integer) evt.getNewValue(); progressBar.setValue(progress); + } else if ("log" == evt.getPropertyName()) { + firePropertyChange("log", evt.getOldValue(), evt.getNewValue()); } } }