diff --git a/uk/ac/sanger/artemis/components/alignment/JamView.java b/uk/ac/sanger/artemis/components/alignment/JamView.java
new file mode 100644
index 0000000000000000000000000000000000000000..e73eaf3171c5020bd8b485f0098686d824e6c81b
--- /dev/null
+++ b/uk/ac/sanger/artemis/components/alignment/JamView.java
@@ -0,0 +1,473 @@
+
+package uk.ac.sanger.artemis.components.alignment;
+
+import java.awt.BasicStroke;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Stroke;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Vector;
+
+import javax.swing.JComboBox;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+
+import uk.ac.sanger.artemis.Entry;
+import uk.ac.sanger.artemis.EntryGroup;
+import uk.ac.sanger.artemis.Options;
+import uk.ac.sanger.artemis.SimpleEntryGroup;
+import uk.ac.sanger.artemis.components.EntryFileDialog;
+import uk.ac.sanger.artemis.components.MessageDialog;
+import uk.ac.sanger.artemis.io.EntryInformation;
+import uk.ac.sanger.artemis.sequence.Bases;
+import uk.ac.sanger.artemis.sequence.NoSequenceException;
+import uk.ac.sanger.artemis.util.Document;
+import uk.ac.sanger.artemis.util.DocumentFactory;
+import uk.ac.sanger.artemis.util.OutOfRangeException;
+
+public class JamView extends JPanel
+{
+ private static final long serialVersionUID = 1L;
+ private List<Read> readsInView;
+ private Hashtable<String, Integer> seqLengths = new Hashtable<String, Integer>();
+ private Vector<String> seqNames = new Vector<String>();
+ private String bam;
+ private String sam;
+ private EntryGroup entryGroup;
+ private JScrollPane jspView;
+ private JComboBox combo;
+ private int nbasesInView;
+
+ public JamView(String bam,
+ String reference,
+ int nbasesInView)
+ {
+ super();
+ setBackground(Color.white);
+ this.bam = bam;
+ this.nbasesInView = nbasesInView;
+
+ if(reference != null)
+ {
+ entryGroup = new SimpleEntryGroup();
+ try
+ {
+ getEntry(reference,entryGroup);
+ }
+ catch (NoSequenceException e)
+ {
+ e.printStackTrace();
+ }
+ }
+ readHeader();
+ }
+
+ private void readHeader()
+ {
+ String cmd[] = { "/Users/tjc/BAM/samtools-0.1.5c/samtools",
+ "view", "-H", bam };
+
+ RunSamTools samtools = new RunSamTools(cmd, null, null);
+
+ if(samtools.getProcessStderr() != null)
+ System.out.println(samtools.getProcessStderr());
+
+ String header = samtools.getProcessStdout();
+
+ StringReader samReader = new StringReader(header);
+ BufferedReader buff = new BufferedReader(samReader);
+
+ String line;
+ try
+ {
+ while((line = buff.readLine()) != null)
+ {
+ if(line.indexOf("LN:") > -1)
+ {
+ String parts[] = line.split("\t");
+ String name = "";
+ int seqLength = 0;
+ for(int i=0; i<parts.length; i++)
+ {
+ if(parts[i].startsWith("LN:"))
+ seqLength = Integer.parseInt( parts[i].substring(3) );
+ else if(parts[i].startsWith("SN:"))
+ name = parts[i].substring(3);
+ }
+ seqLengths.put(name, seqLength);
+ seqNames.add(name);
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void readFromBam(int start, int end)
+ {
+ String refName = (String) combo.getSelectedItem();
+ String cmd[] = { "/Users/tjc/BAM/samtools-0.1.5c/samtools",
+ "view", bam, refName+":"+start+"-"+end };
+
+ for(int i=0; i<cmd.length;i++)
+ System.out.print(cmd[i]+" ");
+ System.out.println();
+ RunSamTools samtools = new RunSamTools(cmd, null, null);
+
+ if(samtools.getProcessStderr() != null)
+ System.out.println(samtools.getProcessStderr());
+
+ sam = samtools.getProcessStdout();
+
+ StringReader samReader = new StringReader(sam);
+ BufferedReader buff = new BufferedReader(samReader);
+ String line;
+ try
+ {
+ if(readsInView == null)
+ readsInView = new Vector<Read>();
+ else
+ readsInView.clear();
+ while((line = buff.readLine()) != null)
+ {
+ String fields[] = line.split("\t");
+
+ Read pread = new Read();
+ pread.qname = fields[0];
+ pread.flag = Integer.parseInt(fields[1]);
+ pread.rname = fields[2];
+ pread.pos = Integer.parseInt(fields[3]);
+ pread.mapq = Short.parseShort(fields[4]);
+ pread.mpos = Integer.parseInt(fields[7]);
+ pread.isize = Integer.parseInt(fields[8]);
+ pread.seq = fields[9];
+ readsInView.add(pread);
+ }
+
+ Collections.sort(readsInView, new ReadComparator());
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Override
+ */
+ public void paintComponent(Graphics g)
+ {
+ super.paintComponent(g);
+ Graphics2D g2 = (Graphics2D)g;
+
+ String refName = (String) combo.getSelectedItem();
+ int seqLength = seqLengths.get(refName);
+
+ float pixPerBase = ((float)getWidth())/(float)(seqLength);
+ double x = jspView.getViewport().getViewRect().getX();
+
+ int start = (int) (seqLength * ( x / getWidth()));
+ int end = (int) (start + (jspView.getViewport().getWidth() / pixPerBase));
+
+ int scaleHeight = 15;
+ readFromBam(start, end);
+
+ drawScale(g2, start, end, pixPerBase);
+
+ Stroke originalStroke = g2.getStroke();
+ Stroke stroke =
+ new BasicStroke (2.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
+
+ for(int i=0; i<readsInView.size(); i++)
+ {
+ Read thisRead = readsInView.get(i);
+ Read nextRead = null;
+
+ if( (thisRead.flag & 0x0001) != 0x0001 ||
+ (thisRead.flag & 0x0008) == 0x0008 ) // not single read
+ continue;
+
+ if(i < readsInView.size()-1)
+ {
+ nextRead = readsInView.get(i+1);
+ i++;
+ }
+
+ if(nextRead != null &&
+ (nextRead.flag & 0x0001) == 0x0001 &&
+ (nextRead.flag & 0x0008) != 0x0008 )
+ {
+ if(thisRead.qname.equals(nextRead.qname))
+ {
+ if( (thisRead.flag & 0x0010) == 0x0010 &&
+ (nextRead.flag & 0x0010) == 0x0010 )
+ g2.setColor(Color.red);
+ else
+ g2.setColor(Color.blue);
+
+ int ypos = (getHeight() - scaleHeight) - ( Math.abs(thisRead.isize) );
+ int thisStart = drawRead(g2, thisRead, pixPerBase, stroke, ypos);
+ int nextStart = drawRead(g2, nextRead, pixPerBase, stroke, ypos);
+ g2.setStroke(originalStroke);
+ g2.setColor(Color.LIGHT_GRAY);
+ g2.drawLine(thisStart, ypos, nextStart, ypos);
+ }
+ else
+ i--;
+ }
+ }
+ }
+
+ private int getBaseAtStartOfView()
+ {
+ String refName = (String) combo.getSelectedItem();
+ int seqLength = seqLengths.get(refName);
+ double x = jspView.getViewport().getViewRect().getX();
+ return (int) (seqLength * ( x / getWidth()));
+ }
+
+ private void drawScale(Graphics2D g2, int start, int end, float pixPerBase)
+ {
+ g2.setColor(Color.black);
+ g2.drawLine( (int)(start*pixPerBase), getHeight()-14, (int)(end*pixPerBase), getHeight()-14);
+ int interval = end-start;
+
+ if(interval > 256000)
+ drawTicks(g2, start, end, pixPerBase, 512000);
+ else if(interval > 64000)
+ drawTicks(g2, start, end, pixPerBase, 12800);
+ else if(interval > 16000)
+ drawTicks(g2, start, end, pixPerBase, 3200);
+ else if(interval > 4000)
+ drawTicks(g2, start, end, pixPerBase, 800);
+ else if(interval > 1000)
+ drawTicks(g2, start, end, pixPerBase, 200);
+ }
+
+ private void drawTicks(Graphics2D g2, int start, int end, float pixPerBase, int division)
+ {
+ int markStart = (Math.round(start/division)*division)+division;
+ int sm = markStart-(division/2);
+
+ if(sm > start)
+ g2.drawLine((int)(sm*pixPerBase), getHeight()-14,(int)(sm*pixPerBase), getHeight()-12);
+
+ for(int m=markStart; m<end; m+=division)
+ {
+ g2.drawString(Integer.toString(m), m*pixPerBase, getHeight()-1);
+ g2.drawLine((int)(m*pixPerBase), getHeight()-14,(int)(m*pixPerBase), getHeight()-11);
+
+ sm = m+(division/2);
+
+ if(sm < end)
+ g2.drawLine((int)(sm*pixPerBase), getHeight()-14,(int)(sm*pixPerBase), getHeight()-12);
+ }
+ }
+
+ private int drawRead(Graphics2D g2, Read read,
+ float pixPerBase, Stroke stroke, int ypos)
+ {
+ int thisStart = (int) (read.pos*pixPerBase);
+ int thisEnd = (int) (thisStart + (read.seq.length()*pixPerBase));
+ g2.setStroke(stroke);
+ g2.drawLine(thisStart, ypos, thisEnd, ypos);
+ return thisStart;
+ }
+
+
+
+
+ public void showJFrame()
+ {
+ this.nbasesInView = nbasesInView;
+ JFrame frame = new JFrame("JamTool");
+
+ combo = new JComboBox(seqNames);
+ combo.setEditable(false);
+ combo.addItemListener(new ItemListener()
+ {
+ public void itemStateChanged(ItemEvent e)
+ {
+ setZoomLevel(JamView.this.nbasesInView);
+ }
+ });
+
+ frame.setPreferredSize(new Dimension(1000,800));
+ setLength(nbasesInView);
+
+ jspView = new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_NEVER,
+ JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
+
+ JPanel panel = (JPanel) frame.getContentPane();
+
+ panel.setLayout(new BorderLayout());
+ panel.add(combo, BorderLayout.NORTH);
+ panel.add(jspView, BorderLayout.CENTER);
+ frame.pack();
+
+ setFocusable(true);
+ requestFocusInWindow();
+
+ addKeyListener(new KeyAdapter()
+ {
+ public void keyPressed(final KeyEvent event)
+ {
+ switch(event.getKeyCode())
+ {
+ case KeyEvent.VK_UP:
+ setZoomLevel( (int) (JamView.this.nbasesInView*1.1) );
+ repaint();
+ break;
+ case KeyEvent.VK_DOWN:
+ setZoomLevel( (int) (JamView.this.nbasesInView*.9) );
+ repaint();
+ break;
+ default:
+ break;
+ }
+ }
+ });
+
+ addFocusListener(new FocusListener()
+ {
+ public void focusGained(FocusEvent fe) {}
+ public void focusLost(FocusEvent fe) {}
+ });
+
+ frame.setVisible(true);
+ }
+
+ private void setZoomLevel(final int nbasesInView)
+ {
+ int startBase = getBaseAtStartOfView();
+
+ this.nbasesInView = nbasesInView;
+ setLength(this.nbasesInView);
+ revalidate();
+ repaint();
+ System.out.println(startBase+" "+getBaseAtStartOfView());
+
+ String refName = (String) combo.getSelectedItem();
+ int seqLength = seqLengths.get(refName);
+
+
+ float pixPerBase = ((float)getWidth())/(float)(seqLength);
+ Point p = jspView.getViewport().getViewPosition();
+ p.x = (int)(startBase*pixPerBase);
+ jspView.getViewport().setViewPosition(p);
+ revalidate();
+ repaint();
+
+ System.out.println(startBase+" "+getBaseAtStartOfView());
+ }
+
+ private void setLength(int basesToShow)
+ {
+ String refName = (String) combo.getSelectedItem();
+ int seqLength = seqLengths.get(refName);
+ float pixPerBase = 1000.f/(float)(basesToShow);
+ setPreferredSize( new Dimension((int) (seqLength*pixPerBase),
+ getSize().height) );
+ }
+
+ /**
+ * Return an Artemis entry from a file
+ * @param entryFileName
+ * @param entryGroup
+ * @return
+ * @throws NoSequenceException
+ */
+ private Entry getEntry(final String entryFileName, final EntryGroup entryGroup)
+ throws NoSequenceException
+ {
+ final Document entry_document = DocumentFactory.makeDocument(entryFileName);
+ final EntryInformation artemis_entry_information =
+ Options.getArtemisEntryInformation();
+
+ System.out.println(entryFileName);
+ final uk.ac.sanger.artemis.io.Entry new_embl_entry =
+ EntryFileDialog.getEntryFromFile(null, entry_document,
+ artemis_entry_information,
+ false);
+
+ if(new_embl_entry == null) // the read failed
+ return null;
+
+ Entry entry = null;
+ try
+ {
+ Bases bases = null;
+ if(entryGroup.getSequenceEntry() != null)
+ bases = entryGroup.getSequenceEntry().getBases();
+ if(bases == null)
+ entry = new Entry(new_embl_entry);
+ else
+ entry = new Entry(bases,new_embl_entry);
+
+ entryGroup.add(entry);
+ }
+ catch(OutOfRangeException e)
+ {
+ new MessageDialog(null, "read failed: one of the features in " +
+ entryFileName + " has an out of range " +
+ "location: " + e.getMessage());
+ }
+ return entry;
+ }
+
+ class Read
+ {
+ String qname; // query name
+ int flag; // bitwise flag
+ String rname; // reference name
+ int pos; // leftmost coordinate
+ short mapq; // MAPping Quality
+ String cigar; // extended CIGAR string
+ String mrnm; // Mate Reference sequence NaMe; �=� if the same as rname
+ int mpos; // leftmost Mate POSition
+ int isize; // inferred Insert SIZE
+ String seq; // query SEQuence; �=� for a match to the reference; n/N/. for ambiguity
+ }
+
+ class ReadComparator implements Comparator
+ {
+ public int compare(Object o1, Object o2)
+ {
+ Read pr1 = (Read) o1;
+ Read pr2 = (Read) o2;
+
+ return pr1.qname.compareTo(pr2.qname);
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ String bam = args[0];
+ int nbasesInView = Integer.parseInt(args[1]);
+ String reference = null;
+ if(args.length > 2)
+ reference = args[2];
+
+ JamView view = new JamView(bam, reference, nbasesInView);
+ view.showJFrame();
+ }
+}
diff --git a/uk/ac/sanger/artemis/components/alignment/RunSamTools.java b/uk/ac/sanger/artemis/components/alignment/RunSamTools.java
new file mode 100644
index 0000000000000000000000000000000000000000..2ac1284d4116bb25aee4456eaa61a74cd737fdd3
--- /dev/null
+++ b/uk/ac/sanger/artemis/components/alignment/RunSamTools.java
@@ -0,0 +1,317 @@
+
+package uk.ac.sanger.artemis.components.alignment;
+
+import java.io.*;
+import javax.swing.JTextArea;
+
+/**
+* Used to run an samtools process this reads stdout and
+* stderr in separate threads.
+*/
+public class RunSamTools
+{
+
+ /** running process */
+ private Process p;
+ /** standard out */
+ private StringBuffer stdout = new StringBuffer();
+ /** standard error */
+ private StringBuffer stderr = new StringBuffer();
+
+ private String initialIOError = null;
+
+ /** running directory */
+ //private File project;
+ /** process status */
+ private String status;
+ private StdoutHandler stdouth;
+ private StderrHandler stderrh;
+ private JTextArea textArea;
+
+
+ /**
+ * @param cmd command to run
+ * @param env environment
+ * @param project running directory
+ */
+ public RunSamTools(String cmd[],
+ String[] envp, File project)
+ {
+ //this.project = project;
+ status = "0";
+
+ Runtime run = Runtime.getRuntime();
+ try
+ {
+ p = run.exec(cmd,envp,project);
+
+ // 2 threads to read in stdout & stderr buffers
+ // to prevent blocking
+ stdouth = new StdoutHandler(this);
+ stderrh = new StderrHandler(this);
+ stdouth.start();
+ stderrh.start();
+ }
+ catch(IOException ioe)
+ {
+ System.err.println("Error executing: "+
+ cmd[0]);
+ initialIOError = ioe.getMessage();
+ status = "1";
+ }
+ }
+
+ /**
+ * Read in the process stderr.
+ */
+ private void readProcessStderr()
+ {
+ BufferedInputStream stderrStream = null;
+ BufferedReader stderrRead = null;
+ try
+ {
+ //String line;
+ stderrStream =
+ new BufferedInputStream(p.getErrorStream());
+ stderrRead =
+ new BufferedReader(new InputStreamReader(stderrStream));
+ char c[] = new char[100];
+ int nc = 0;
+
+ while((nc = stderrRead.read(c,0,100)) != -1)
+ stderr = stderr.append(new String(c,0,nc));
+ }
+ catch (IOException io)
+ {
+ System.err.println("RunEmbossApplication2: Error in "+
+ "collecting standard out");
+ }
+ finally
+ {
+ try
+ {
+ if(stderrStream!=null)
+ stderrStream.close();
+ }
+ catch(IOException ioe)
+ {
+ System.err.println("RunEmbossApplication2: Error closing stream");
+ }
+ try
+ {
+ if(stderrRead!=null)
+ stderrRead.close();
+ }
+ catch(IOException ioe)
+ {
+ System.err.println("RunEmbossApplication2: Error closing reader");
+ }
+ }
+
+ return;
+ }
+
+ /**
+ * Read in the process stdout.
+ */
+ private void readProcessStdout()
+ {
+ BufferedInputStream stdoutStream = null;
+ BufferedReader stdoutRead = null;
+ try
+ {
+ //String line;
+ stdoutStream =
+ new BufferedInputStream(p.getInputStream());
+ stdoutRead =
+ new BufferedReader(new InputStreamReader(stdoutStream));
+
+ char c[] = new char[200];
+ int nc = 0;
+ String chunk;
+
+ while((nc = stdoutRead.read(c,0,200)) != -1)
+ {
+ chunk = new String(c,0,nc);
+ stdout = stdout.append(chunk);
+ if(textArea != null)
+ {
+ textArea.append(chunk);
+ textArea.setCaretPosition(textArea.getDocument().getLength());
+ }
+ }
+ }
+ catch (IOException io)
+ {
+ System.err.println("RunEmbossApplication2: Error in "+
+ "collecting standard out");
+ }
+ finally
+ {
+ try
+ {
+ if(stdoutStream!=null)
+ stdoutStream.close();
+ }
+ catch(IOException ioe)
+ {
+ System.err.println("RunEmbossApplication2: Error closing stream");
+ }
+ try
+ {
+ if(stdoutRead!=null)
+ stdoutRead.close();
+ }
+ catch(IOException ioe)
+ {
+ System.err.println("RunEmbossApplication2: Error closing reader");
+ }
+ }
+
+ return;
+ }
+
+ /**
+ * This method can be called after the process has completed
+ * to write the stdout to the project directory.
+ */
+// private void writeStdout()
+// {
+// if(project != null)
+// {
+// PrintWriter out = null;
+// String fname = "";
+// try
+// {
+// fname = project.getCanonicalPath() +
+// "/application_stdout";
+// File so = new File(fname);
+//
+// if(!so.exists())
+// so.createNewFile();
+//
+// out = new PrintWriter(new FileWriter(fname,true));
+// out.println(stdout);
+// }
+// catch(IOException ioe)
+// {
+// System.err.println("RunEmbossApplication2: Error writing" +
+// fname);
+// }
+// finally
+// {
+// if(out!=null)
+// out.close();
+// }
+// }
+// }
+
+ /**
+ * @return standard out
+ */
+ public String getProcessStdout()
+ {
+ try
+ {
+ // make sure we hang around for stdout
+ while(stdouth.isAlive())
+ Thread.sleep(10);
+ }
+ catch(InterruptedException ie)
+ {
+ ie.printStackTrace();
+ }
+
+ return stdout.toString();
+ }
+
+ /**
+ * @return stderr
+ */
+ public String getProcessStderr()
+ {
+ try
+ {
+ // make sure we hang around for stdout
+ while(stderrh.isAlive())
+ Thread.sleep(10);
+ }
+ catch(InterruptedException ie)
+ {
+ ie.printStackTrace();
+ }
+
+ return new String(stderr.toString().trim());
+ }
+
+ /**
+ * Wait for the process to end
+ */
+ public void waitFor()
+ {
+ try
+ {
+ int exitVal = p.waitFor();
+
+ if(exitVal != 0)
+ System.out.println("Exit value:: "+exitVal);
+ }
+ catch(InterruptedException ie)
+ {
+ ie.printStackTrace();
+ }
+ }
+
+ /**
+ * @return process
+ */
+ public Process getProcess()
+ {
+ return p;
+ }
+
+ /**
+ * @return status
+ */
+ public String getStatus()
+ {
+ return status;
+ }
+
+ class StdoutHandler extends Thread
+ {
+ RunSamTools rea;
+
+ protected StdoutHandler(RunSamTools rea)
+ {
+ this.rea = rea;
+ }
+
+ public void run()
+ {
+ rea.readProcessStdout();
+ }
+ }
+
+ class StderrHandler extends Thread
+ {
+ RunSamTools rea;
+
+ protected StderrHandler(RunSamTools rea)
+ {
+ this.rea = rea;
+ }
+
+ public void run()
+ {
+ rea.readProcessStderr();
+ }
+ }
+
+ public String getInitialIOError()
+ {
+ return initialIOError;
+ }
+
+}
+