From d579f105e3feca4b77ac93c52bb45f8cc39c39cd Mon Sep 17 00:00:00 2001 From: gmungoc Date: Tue, 22 Dec 2015 09:15:40 +0000 Subject: [PATCH] JAL-391 new Calculate options to Reverse, Reverse Complement nucleotide --- resources/lang/Messages.properties | 2 + src/jalview/analysis/Dna.java | 150 ++++++++++++++++++++++++++++++++++++ src/jalview/gui/AlignFrame.java | 28 ++++++- src/jalview/jbgui/GAlignFrame.java | 37 ++++++++- test/jalview/analysis/DnaTest.java | 54 +++++++++++++ 5 files changed, 267 insertions(+), 4 deletions(-) diff --git a/resources/lang/Messages.properties b/resources/lang/Messages.properties index 54370d8..42dc0f7 100644 --- a/resources/lang/Messages.properties +++ b/resources/lang/Messages.properties @@ -704,6 +704,8 @@ label.load_tree_for_sequence_set = Load a tree for this sequence set label.export_image = Export Image label.vamsas_store = VAMSAS store label.translate_cDNA = Translate as cDNA +label.reverse = Reverse +label.reverse_complement = Reverse Complement label.linked_view_title = Linked cDNA and protein view label.align = Align label.extract_scores = Extract Scores diff --git a/src/jalview/analysis/Dna.java b/src/jalview/analysis/Dna.java index 9ce00cc..f8bb9f9 100644 --- a/src/jalview/analysis/Dna.java +++ b/src/jalview/analysis/Dna.java @@ -806,4 +806,154 @@ public class Dna } } } + + /** + * Returns an alignment consisting of the reversed (and optionally + * complemented) sequences set in this object's constructor + * + * @param complement + * @return + */ + public AlignmentI reverseCdna(boolean complement) + { + int sSize = selection.size(); + List reversed = new ArrayList(); + for (int s = 0; s < sSize; s++) + { + SequenceI newseq = reverseSequence(selection.get(s).getName(), + seqstring[s], complement); + + if (newseq != null) + { + reversed.add(newseq); + } + } + + SequenceI[] newseqs = reversed.toArray(new SequenceI[reversed.size()]); + AlignmentI al = new Alignment(newseqs); + ((Alignment) al).createDatasetAlignment(); + return al; + } + + /** + * Returns a reversed, and optionally complemented, sequence. The new + * sequence's name is the original name with "|rev" or "|revcomp" appended. + * aAcCgGtT and DNA ambiguity codes are complemented, any other characters are + * left unchanged. + * + * @param seq + * @param complement + * @return + */ + public static SequenceI reverseSequence(String seqName, String sequence, + boolean complement) + { + String newName = seqName + "|rev" + (complement ? "comp" : ""); + char[] originalSequence = sequence.toCharArray(); + int length = originalSequence.length; + char[] reversedSequence = new char[length]; + + for (int i = 0; i < length; i++) + { + reversedSequence[length - i - 1] = complement ? getComplement(originalSequence[i]) + : originalSequence[i]; + } + SequenceI reversed = new Sequence(newName, reversedSequence, 1, length); + return reversed; + } + + /** + * Returns dna complement (preserving case) for aAcCgGtTuU. Ambiguity codes + * are treated as on http://reverse-complement.com/. Anything else is left + * unchanged. + * + * @param c + * @return + */ + public static char getComplement(char c) + { + char result = c; + switch (c) { + case 'a': + result = 't'; + break; + case 'A': + result = 'T'; + break; + case 'c': + result = 'g'; + break; + case 'C': + result = 'G'; + break; + case 'g': + result = 'c'; + break; + case 'G': + result = 'C'; + break; + case 't': + result = 'a'; + break; + case 'T': + result = 'A'; + break; + case 'u': + result = 'a'; + break; + case 'U': + result = 'A'; + break; + case 'r': + result = 'y'; + break; + case 'R': + result = 'Y'; + break; + case 'y': + result = 'r'; + break; + case 'Y': + result = 'R'; + break; + case 'k': + result = 'm'; + break; + case 'K': + result = 'M'; + break; + case 'm': + result = 'k'; + break; + case 'M': + result = 'K'; + break; + case 'b': + result = 'v'; + break; + case 'B': + result = 'V'; + break; + case 'v': + result = 'b'; + break; + case 'V': + result = 'B'; + break; + case 'd': + result = 'h'; + break; + case 'D': + result = 'H'; + break; + case 'h': + result = 'd'; + break; + case 'H': + result = 'D'; + break; + } + + return result; + } } diff --git a/src/jalview/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java index 4e5083d..9113b66 100644 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@ -131,7 +131,6 @@ import java.util.Deque; import java.util.Enumeration; import java.util.Hashtable; import java.util.List; -import java.util.Set; import java.util.Vector; import javax.swing.JCheckBoxMenuItem; @@ -828,6 +827,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, public void setGUINucleotide(boolean nucleotide) { showTranslation.setVisible(nucleotide); + showReverse.setVisible(nucleotide); + showReverseComplement.setVisible(nucleotide); conservationMenuItem.setEnabled(!nucleotide); modifyConservation.setEnabled(!nucleotide); showGroupConservation.setEnabled(!nucleotide); @@ -4783,7 +4784,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, // TODO 1: no mappings are set up for EMBL product // TODO 2: if they were, should add them to protein alignment, not // dna - Set cf = prods.getCodonFrames(); + List cf = prods.getCodonFrames(); for (AlignedCodonFrame acf : cf) { al.addCodonFrame(acf); @@ -5960,7 +5961,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { // TODO no longer a menu action - refactor as required final AlignmentI alignment = getViewport().getAlignment(); - Set mappings = alignment.getCodonFrames(); + List mappings = alignment.getCodonFrames(); if (mappings == null) { return; @@ -6015,6 +6016,27 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, sf.setComplementVisible(this, show); } } + + /** + * Generate the reverse (optionally complemented) of the selected sequences, + * and add them to the alignment + */ + @Override + protected void showReverse_actionPerformed(boolean complement) + { + AlignmentI al = null; + try + { + Dna dna = new Dna(viewport, viewport.getViewAsVisibleContigs(true)); + + al = dna.reverseCdna(complement); + viewport.addAlignment(al, ""); + } catch (Exception ex) + { + System.err.println(ex.getMessage()); + return; + } + } } class PrintThread extends Thread diff --git a/src/jalview/jbgui/GAlignFrame.java b/src/jalview/jbgui/GAlignFrame.java index 73d34c2..205b9c6 100755 --- a/src/jalview/jbgui/GAlignFrame.java +++ b/src/jalview/jbgui/GAlignFrame.java @@ -173,6 +173,10 @@ public class GAlignFrame extends JInternalFrame protected JMenuItem showTranslation = new JMenuItem(); + protected JMenuItem showReverse = new JMenuItem(); + + protected JMenuItem showReverseComplement = new JMenuItem(); + protected JMenu showProducts = new JMenu(); protected JMenuItem rnahelicesColour = new JMenuItem(); @@ -1686,6 +1690,25 @@ public class GAlignFrame extends JInternalFrame showTranslation_actionPerformed(e); } }); + showReverse.setText(MessageManager.getString("label.reverse")); + showReverse.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + showReverse_actionPerformed(false); + } + }); + showReverseComplement.setText(MessageManager + .getString("label.reverse_complement")); + showReverseComplement.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + showReverse_actionPerformed(true); + } + }); JMenuItem extractScores = new JMenuItem( MessageManager.getString("label.extract_scores")); @@ -2252,6 +2275,8 @@ public class GAlignFrame extends JInternalFrame calculateMenu.add(PCAMenuItem); calculateMenu.addSeparator(); calculateMenu.add(showTranslation); + calculateMenu.add(showReverse); + calculateMenu.add(showReverseComplement); calculateMenu.add(showProducts); calculateMenu.add(autoCalculate); calculateMenu.add(sortByTree); @@ -2320,6 +2345,16 @@ public class GAlignFrame extends JInternalFrame } /** + * Generate the reverse sequence (or reverse complement if the flag is true) + * and add it to the alignment + * + * @param complement + */ + protected void showReverse_actionPerformed(boolean complement) + { + } + + /** * Adds the given action listener and key accelerator to the given menu item. * Also saves in a lookup table to support lookup of action by key stroke. * @@ -3107,7 +3142,7 @@ public class GAlignFrame extends JInternalFrame return this.splitFrame; } - protected void showComplement_actionPerformed(boolean state) + protected void showComplement_actionPerformed(boolean complement) { } } diff --git a/test/jalview/analysis/DnaTest.java b/test/jalview/analysis/DnaTest.java index 8f878f0..9a4c357 100644 --- a/test/jalview/analysis/DnaTest.java +++ b/test/jalview/analysis/DnaTest.java @@ -457,4 +457,58 @@ public class DnaTest assertEquals("[0, 2, 5]", convertCodon("A-A--A").toString()); assertEquals("[1, 3, 4]", convertCodon("-A-AA-").toString()); } + + /** + * Test dna complementing + */ + @Test(groups = "Functional") + public void testGetComplement() + { + assertEquals('t', Dna.getComplement('a')); + assertEquals('T', Dna.getComplement('A')); + assertEquals('a', Dna.getComplement('t')); + assertEquals('A', Dna.getComplement('T')); + assertEquals('c', Dna.getComplement('g')); + assertEquals('C', Dna.getComplement('G')); + assertEquals('g', Dna.getComplement('c')); + assertEquals('G', Dna.getComplement('C')); + // note uU --> aA but not vice versa + assertEquals('a', Dna.getComplement('u')); + assertEquals('A', Dna.getComplement('U')); + // ambiguity codes, see http://www.bioinformatics.org/sms/iupac.html + assertEquals('r', Dna.getComplement('y')); + assertEquals('R', Dna.getComplement('Y')); + assertEquals('y', Dna.getComplement('r')); + assertEquals('Y', Dna.getComplement('R')); + assertEquals('k', Dna.getComplement('m')); + assertEquals('K', Dna.getComplement('M')); + assertEquals('m', Dna.getComplement('k')); + assertEquals('M', Dna.getComplement('K')); + assertEquals('b', Dna.getComplement('v')); + assertEquals('B', Dna.getComplement('V')); + assertEquals('v', Dna.getComplement('b')); + assertEquals('V', Dna.getComplement('B')); + assertEquals('d', Dna.getComplement('h')); + assertEquals('D', Dna.getComplement('H')); + assertEquals('h', Dna.getComplement('d')); + assertEquals('H', Dna.getComplement('D')); + assertEquals('Q', Dna.getComplement('Q')); + } + + @Test(groups = "Functional") + public void testReverseSequence() + { + String seq = "AcGtUrYkMbVdHNX"; + + // reverse: + SequenceI reversed = Dna.reverseSequence("Seq1", seq, false); + assertEquals(new StringBuilder(seq).reverse() + .toString(), reversed.getSequenceAsString()); + assertEquals("Seq1|rev", reversed.getName()); + + // reverse complement: + SequenceI revcomp = Dna.reverseSequence("Seq1", seq, true); + assertEquals("XNDhBvKmRyAaCgT", revcomp.getSequenceAsString()); + assertEquals("Seq1|revcomp", revcomp.getName()); + } } -- 1.7.10.2