label.export_image = Export Image
label.vamsas_store = VAMSAS store
label.translate_cDNA = Translate cDNA
+label.cDNA = cDNA
+label.associate = Associate
+label.align = Align
label.extract_scores = Extract Scores
label.get_cross_refs = Get Cross References
label.sort_alignment_new_tree = Sort Alignment With New Tree
label.normalise_logo = Normalise Logo
label.no_colour_selection_in_scheme = Please, make a colour selection before to apply colour scheme
label.no_colour_selection_warn = Error saving colour scheme
+label.nonstandard_translation = Non-standard translation
+warn.nonstandard_translation = Non-standard translation(s) detected at {0}.<br>Do you wish to proceed?
+label.cdna_realign = Warning
+warn.cdna_realign = cDNA will be realigned if necessary to match the protein alignment.<br>Do you wish to proceed?
import java.util.List;
import java.util.Vector;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.Vector;
+
public class Dna
{
/**
AlignmentI al = new Alignment(newseqs);
al.padGaps(); // ensure we look aligned.
al.setDataset(dataset);
- translateAlignedAnnotations(annotations, al, codons);
+ // translateAlignedAnnotations(annotations, al, codons);
al.addCodonFrame(codons);
return al;
}
{
return dataset;
}
+
+ /**
+ * Answers true if the supplied alignment has the same number of sequences,
+ * and they are of equivalent length, ignoring gaps. Alignments should be of
+ * the same type (protein/nucleotide) or different types with 3:1 length
+ * scaling.
+ *
+ * @param al
+ */
+ @Override
+ public boolean isMappableTo(AlignmentI al)
+ {
+ int thisCodonScale = this.isNucleotide() ? 1 : 3;
+ int thatCodonScale = al.isNucleotide() ? 1 : 3;
+ if (this == al || this.getHeight() != al.getHeight())
+ {
+ return false;
+ }
+ int i = 0;
+ for (SequenceI seq : this.getSequences())
+ {
+ final int thisSequenceDnaLength = seq.getDatasetSequence()
+ .getLength() * thisCodonScale;
+ final int thatSequenceDnaLength = al.getSequenceAt(i)
+ .getDatasetSequence().getLength()
+ * thatCodonScale;
+ if (thisSequenceDnaLength != thatSequenceDnaLength)
+ {
+ return false;
+ }
+ i++;
+ }
+ return true;
+ }
+
+ /**
+ * Align this alignment the same as the given one. If both of the same type
+ * (nucleotide/protein) then align both identically. If this is nucleotide and
+ * the other is protein, make 3 gaps for each gap in the protein sequences. If
+ * this is protein and the other is nucleotide, insert a gap for each 3 gaps
+ * (or part thereof) between nucleotide bases. The two alignments should be
+ * compatible in height and lengths, but if not, then discrepancies will be
+ * ignored with unpredictable results.
+ *
+ * @param al
+ * @throws UnsupportedOperation
+ * if alignment of protein from cDNA is requested (not yet
+ * implemented)
+ */
+ @Override
+ public void alignAs(AlignmentI al)
+ {
+ boolean thisIsNucleotide = this.isNucleotide();
+ boolean thatIsProtein = !al.isNucleotide();
+ if (!thatIsProtein && !thisIsNucleotide)
+ {
+ throw new UnsupportedOperationException(
+ "Alignment of protein from cDNA not implemented");
+ }
+ char thisGapChar = this.getGapCharacter();
+ char thatGapChar = al.getGapCharacter();
+ String gap = thisIsNucleotide && thatIsProtein ? String
+ .valueOf(new char[]
+ { thisGapChar, thisGapChar, thisGapChar }) : String
+ .valueOf(thisGapChar);
+ int ratio = thisIsNucleotide && thatIsProtein ? 3 : 1;
+ int i = 0;
+ for (SequenceI seq : this.getSequences())
+ {
+ SequenceI other = al.getSequenceAt(i++);
+ if (other == null)
+ {
+ continue;
+ }
+ char[] thisDs = seq.getDatasetSequence().getSequence();
+ char[] thatDs = other.getSequence();
+ StringBuilder thisAligned = new StringBuilder(2 * thisDs.length);
+ int thisDsPosition = 0;
+ for (char thatChar : thatDs)
+ {
+ if (thatChar == thatGapChar)
+ {
+ /*
+ * Add (equivalent of) a gap
+ */
+ thisAligned.append(gap);
+ }
+ else
+ {
+ /*
+ * Add (equivalent of) a residue
+ */
+ for (int j = 0; j < ratio && thisDsPosition < thisDs.length; j++)
+ {
+ thisAligned.append(thisDs[thisDsPosition++]);
+ }
+ }
+ }
+ /*
+ * Include any 'extra' residues (there shouldn't be).
+ */
+ while (thisDsPosition < thisDs.length)
+ {
+ thisAligned.append(thisDs[thisDsPosition++]);
+ }
+ seq.setSequence(new String(thisAligned));
+ }
+ }
}
* @param alignmentAnnotation
*/
public void validateAnnotation(AlignmentAnnotation alignmentAnnotation);
+
+ /**
+ * Answers true if the two alignments residues could be put into
+ * correspondence, i.e. the supplied alignment has the same number of
+ * sequences, and they are of equivalent length, ignoring gaps. Alignments
+ * should be of the same type (protein/nucleotide) or different types with 3:1
+ * length scaling.
+ *
+ * @param al
+ */
+ public boolean isMappableTo(AlignmentI al);
+
+ /**
+ * Align this alignment the same as the given one. If both of the same type
+ * (nucleotide/protein) then align both identically. If this is nucleotide and
+ * the other is protein, make 3 gaps for each gap in the protein sequences. If
+ * this is protein and the other is nucleotide, insert a gap for each 3 gaps
+ * (or part thereof) between nucleotide bases.
+ *
+ * @param al
+ */
+ public void alignAs(AlignmentI al);
}
import jalview.schemes.TurnColourScheme;
import jalview.schemes.UserColourScheme;
import jalview.schemes.ZappoColourScheme;
+import jalview.structure.StructureSelectionManager;
+import jalview.util.MapList;
import jalview.util.MessageManager;
import jalview.ws.jws1.Discoverer;
import jalview.ws.jws2.Jws2Discoverer;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Hashtable;
+import java.util.Iterator;
import java.util.List;
import java.util.Vector;
public void setGUINucleotide(boolean nucleotide)
{
showTranslation.setVisible(nucleotide);
+ cdna.setVisible(!nucleotide);
+ configureCdnaMenu();
conservationMenuItem.setEnabled(!nucleotide);
modifyConservation.setEnabled(!nucleotide);
showGroupConservation.setEnabled(!nucleotide);
rnahelicesColour.setEnabled(nucleotide);
purinePyrimidineColour.setEnabled(nucleotide);
- // Remember AlignFrame always starts as protein
- // if (!nucleotide)
+ }
+
+ /**
+ * Add any suitable options to the 'cDNA' sub-menu. Options may be to
+ * associate a cDNA alignment, or to align an associated alignment. To be
+ * suitable for association, an AlignFrame has to be nucleotide, and have the
+ * right number of sequences of corresponding length to this one.
+ */
+ protected void configureCdnaMenu()
+ {
+ cdna.removeAll();
+
+ /*
+ * Identify candidates for 'associate cDNA', add to menu.
+ */
+ List<AlignFrame> candidates = getCdnaCandidates();
+ for (final AlignFrame candidate : candidates)
+ {
+ final String text = MessageManager.getString("label.associate") + " "
+ + candidate.getTitle();
+ JMenuItem option = new JMenuItem(text);
+ option.addActionListener(new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ associateCdna(candidate);
+ }
+ });
+ cdna.add(option);
+ }
+
+ /*
+ * Identify candidates for 'align cDNA', add to menu.
+ */
+ final AlignFrame[] alignframes = Desktop.getAlignframes();
+ if (alignframes != null)
+ {
+ for (final AlignFrame af : alignframes)
+ {
+ if (af != this)
+ {
+ if (this.viewport.getStructureSelectionManager()
+ .hasCommandListener(af.viewport))
+ {
+ final String text = MessageManager.getString("label.align")
+ + " " + af.getTitle();
+ JMenuItem option = new JMenuItem(text);
+ option.addActionListener(new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ af.alignPanel.getAlignment().alignAs(
+ AlignFrame.this.alignPanel.getAlignment());
+ af.viewport.alignmentChanged(af.alignPanel);
+ }
+ });
+ cdna.add(option);
+ }
+ }
+ }
+ }
+
+ cdna.setEnabled(cdna.getMenuComponentCount() > 0);
+ }
+
+ /**
+ * Returns a list of AlignFrame which are valid candidates for being the cDNA
+ * to map to this (protein) alignment. Valid means a nucleotide alignment with
+ * matching number of sequences and sequence lengths (excluding gaps).
+ *
+ * @return
+ */
+ protected List<AlignFrame> getCdnaCandidates()
+ {
+ List<AlignFrame> result = new ArrayList<AlignFrame>();
+ if (this.alignPanel != null)
+ {
+ AlignmentI thisAlignment = this.alignPanel.getAlignment();
+ if (thisAlignment == null || thisAlignment.isNucleotide())
+ {
+ return result;
+ }
+ final AlignFrame[] alignframes = Desktop.getAlignframes();
+ if (alignframes != null)
+ {
+ for (AlignFrame af : alignframes)
+ {
+ if (af.alignPanel != null)
+ {
+ final AlignmentI thatAlignment = af.alignPanel.getAlignment();
+ if (thatAlignment.isNucleotide()
+ && thisAlignment.isMappableTo(thatAlignment))
+ {
+ // TODO exclude an AlignFrame which is already mapped to this one
+ // simple version: exclude if already a CommandListener (should
+ // cover most cases but not all)
+ if (!this.viewport.getStructureSelectionManager()
+ .hasCommandListener(af.viewport))
+ {
+ result.add(af);
+ }
+ }
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Build the codon mappings between the given (nucleotide) alignment and this
+ * (protein) alignment. Also make the cDNA alignment a CommandListener for the
+ * protein alignment so that edits are mirrored. The alignments must have the
+ * same number, and equivalent lengths, of (unaligned) sequence.
+ *
+ * @param cdna
+ * @throws IllegalStateException
+ * if sequence counts or lengths are incompatible
+ */
+ protected void associateCdna(AlignFrame cdna)
+ {
+ /*
+ * Warn that cDNA may be realigned to match protein
+ */
+ // int confirm = JOptionPane.showConfirmDialog(
+ // this,
+ // JvSwingUtils.wrapTooltip(true,
+ // MessageManager.getString("warn.cdna_realign")),
+ // MessageManager.getString("label.cdna_realign"),
+ // JOptionPane.OK_CANCEL_OPTION);
+ // if (confirm == JOptionPane.CANCEL_OPTION
+ // || confirm == JOptionPane.CLOSED_OPTION)
// {
- // showTr
- // calculateMenu.remove(calculateMenu.getItemCount() - 2);
+ // return;
// }
+
+ final AlignmentI aaAlignment = this.alignPanel.getAlignment();
+ Iterator<SequenceI> thisSeqs = aaAlignment
+ .getSequences().iterator();
+ Iterator<SequenceI> cdnaSeqs = cdna.alignPanel.getAlignment()
+ .getSequences().iterator();
+ AlignedCodonFrame acf = new AlignedCodonFrame(aaAlignment.getWidth());
+ while (thisSeqs.hasNext())
+ {
+ if (!cdnaSeqs.hasNext())
+ {
+ throw new IllegalStateException("Too few sequences to map");
+ }
+ final SequenceI aaSeq = thisSeqs.next();
+ String aaSeqString = aaSeq.getDatasetSequence()
+ .getSequenceAsString();
+ final SequenceI cdnaSeq = cdnaSeqs.next();
+ String cdnaSeqString = cdnaSeq.getDatasetSequence()
+ .getSequenceAsString();
+ final int aaLength = aaSeqString.length();
+ final int cdnaLength = cdnaSeqString.length();
+ if (cdnaLength != 3 * aaLength)
+ {
+ throw new IllegalStateException(
+ "Protein/cDNA lengths don't match: " + aaLength + "/"
+ + cdnaLength);
+ }
+
+ /*
+ * Warn if mapping includes non-standard translations
+ */
+ if (!doTranslationWarningCheck(aaSeq.getName(), aaSeqString,
+ cdnaSeqString))
+ {
+ return;
+ }
+
+ MapList map = new MapList(new int[]
+ { 1, cdnaLength }, new int[]
+ { 1, aaLength }, 3, 1);
+ acf.addMap(cdnaSeq, aaSeq, map);
+
+ aaAlignment.addCodonFrame(acf);
+
+ final StructureSelectionManager ssm = StructureSelectionManager
+ .getStructureSelectionManager(Desktop.instance);
+ ssm.addMappings(aaAlignment.getCodonFrames());
+ ssm.addCommandListener(cdna.getViewport());
+
+ /*
+ * Rebuild 'associate cDna' menu so it now excludes the one just
+ * associated.
+ */
+ configureCdnaMenu();
+ }
+ }
+
+ /**
+ * Show a warning if any non-standard cDNA to protein would result from
+ * mapping the sequences.
+ *
+ * @param aaSeqName
+ * @param aaSeqString
+ * @param aaSeqString
+ * @return true if no warning, or it is accepted, false if user chooses not to
+ * proceed.
+ */
+ protected boolean doTranslationWarningCheck(String aaSeqName,
+ String aaSeqString, String cdnaSeqString)
+ {
+ final int aaLength = aaSeqString.length();
+ boolean warning = false;
+ String msg = aaSeqName;
+ for (int i = 0; i < aaLength; i++)
+ {
+ String codon = cdnaSeqString.substring(i * 3, i * 3 + 3);
+ String aa = ResidueProperties.codonTranslate(codon);
+ if (!(aa.charAt(0) == aaSeqString.charAt(i)))
+ {
+ warning = true;
+ msg += ":" + (i + 1) + ":" + aaSeqString.charAt(i) + "/" + codon
+ + ":" + aa;
+ break;
+ }
+ }
+ if (warning)
+ {
+ final String txt = JvSwingUtils.wrapTooltip(true, MessageManager
+ .formatMessage("warn.nonstandard_translation", msg));
+ int confirm = JOptionPane.showConfirmDialog(this, txt,
+ MessageManager.getString("label.nonstandard_translation"),
+ JOptionPane.OK_CANCEL_OPTION);
+ if (confirm == JOptionPane.CANCEL_OPTION
+ || confirm == JOptionPane.CLOSED_OPTION)
+ {
+ return false;
+ }
+ }
+ return true;
}
/**
- * set up menus for the currently viewport. This may be called after any
+ * set up menus for the current viewport. This may be called after any
* operation that affects the data in the current view (selection changed,
* etc) to update the menus to reflect the new state.
*/
package jalview.gui;
import jalview.bin.Cache;
+import jalview.datamodel.AlignmentI;
import jalview.io.FileLoader;
import jalview.io.FormatAdapter;
import jalview.io.IdentifyFile;
import java.net.URL;
import java.util.ArrayList;
import java.util.Hashtable;
+import java.util.List;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.concurrent.ExecutorService;
static final int yOffset = 30;
+ private static final int THREE = 3;
+
public static jalview.ws.jws1.Discoverer discoverer;
public static Object[] jalviewClipboard;
protected JMenuItem showTranslation = new JMenuItem();
+ protected JMenu cdna = new JMenu();
+
protected JMenuItem extractScores = new JMenuItem();
protected JMenuItem expandAlignment = new JMenuItem();
showTranslation_actionPerformed(e);
}
});
+ cdna.setText(MessageManager.getString("label.cDNA"));
extractScores.setText(MessageManager.getString("label.extract_scores")
+ "...");
extractScores.addActionListener(new ActionListener()
calculateMenu.add(PCAMenuItem);
calculateMenu.addSeparator();
calculateMenu.add(showTranslation);
+ calculateMenu.add(cdna);
calculateMenu.add(showProducts);
calculateMenu.add(autoCalculate);
calculateMenu.add(sortByTree);
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import jalview.io.AppletFormatAdapter;
+import jalview.io.FormatAdapter;
import java.io.IOException;
import java.util.Iterator;
"D.melanogaster.3 G.UGGCGCU..UAUGACGCA\n" +
"#=GR D.melanogaster.3 SS (.(((...(....(((((((\n" +
"//";
- // @formatter:on
+ private static final String TEST_DATA2 =
+ ">TEST21 test21\n" +
+ "AC-GG--CUC-CAA-CT\n" +
+ ">TEST22 test22\n" +
+ "-CG-TTA--ACG---AAGT\n";
+
+ private static final String TEST_DATA3 =
+ ">TEST31 test31\n" +
+ "K-QY--L\n" +
+ ">TEST32 test32\n" +
+ "-R-FP-W-\n";
+
+ private static final String TEST_DATA4 =
+ ">TEST41 test41\n" +
+ "GCTCGUCGTACT\n" +
+ ">TEST42 test42\n" +
+ "GGGTCAGGCAGT\n";
+ // @formatter:on
private Alignment al;
@Before
public void setUp() throws IOException
{
- al = new jalview.io.FormatAdapter().readFile(TEST_DATA,
+ al = new FormatAdapter().readFile(TEST_DATA,
AppletFormatAdapter.PASTE, "STH");
- for (int i = 0; i < al.getSequencesArray().length; ++i)
+ int i = 0;
+ for (AlignmentAnnotation ann : al.getAlignmentAnnotation())
{
- al.addAnnotation(al.getSequenceAt(i).getAnnotation()[0]);
- al.getSequenceAt(i).getAnnotation()[0].setCalcId("CalcIdFor"
+ ann.setCalcId("CalcIdFor"
+ al.getSequenceAt(i).getName());
+ i++;
}
}
assertEquals("D.melanogaster.2", ann.sequenceRef.getName());
assertFalse(iter.hasNext());
}
+
+ /**
+ * Tests for method that checks for alignment 'mappability'.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testIsMappableTo() throws IOException
+ {
+ al = new FormatAdapter().readFile(TEST_DATA2,
+ AppletFormatAdapter.PASTE, "FASTA");
+ al.setDataset(null);
+
+ // not mappable to self
+ assertFalse(al.isMappableTo(al));
+
+ // dna mappable to protein and vice versa
+ AlignmentI alp = new FormatAdapter().readFile(TEST_DATA3,
+ AppletFormatAdapter.PASTE, "FASTA");
+ alp.setDataset(null);
+ assertTrue(al.isMappableTo(alp));
+ assertTrue(alp.isMappableTo(al));
+ assertFalse(alp.isMappableTo(alp));
+
+ // not mappable if any sequence length mismatch
+ alp.getSequenceAt(1).setSequence("-R--FP-");
+ alp.getSequenceAt(1).setDatasetSequence(new Sequence("", "RFP"));
+ assertFalse(alp.isMappableTo(al));
+ assertFalse(al.isMappableTo(alp));
+
+ // not mappable if number of sequences differs
+ alp.deleteSequence(1);
+ assertFalse(alp.isMappableTo(al));
+ assertFalse(al.isMappableTo(alp));
+ }
+
+ /**
+ * Tests for realigning as per a supplied alignment.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testAlignAs_dnaAsDna() throws IOException
+ {
+ // aligned cDNA:
+ Alignment al1 = new FormatAdapter().readFile(TEST_DATA2,
+ AppletFormatAdapter.PASTE, "FASTA");
+ al1.setDataset(null);
+ // unaligned cDNA:
+ Alignment al2 = new FormatAdapter().readFile(TEST_DATA4,
+ AppletFormatAdapter.PASTE, "FASTA");
+ al2.setDataset(null);
+
+ al2.alignAs(al1);
+ assertEquals("GC-TC--GUC-GTA-CT", al2.getSequenceAt(0)
+ .getSequenceAsString());
+ assertEquals("-GG-GTC--AGG---CAGT", al2.getSequenceAt(1)
+ .getSequenceAsString());
+ }
+
+ /**
+ * Aligning protein from cDNA yet to be implemented.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testAlignAs_proteinAsCdna() throws IOException
+ {
+ // aligned cDNA:
+ Alignment al1 = new FormatAdapter().readFile(TEST_DATA2,
+ AppletFormatAdapter.PASTE, "FASTA");
+ al1.setDataset(null);
+ // unaligned cDNA:
+ Alignment al2 = new FormatAdapter().readFile(TEST_DATA3,
+ AppletFormatAdapter.PASTE, "FASTA");
+ al2.setDataset(null);
+
+ try
+ {
+ al2.alignAs(al1);
+ fail("No exception thrown");
+ } catch (UnsupportedOperationException e)
+ {
+ // expected;
+ }
+ }
+
+ /**
+ * Test aligning cdna as per protein alignment.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testAlignAs_cdnaAsProtein() throws IOException
+ {
+ // aligned cDNA:
+ Alignment al1 = new FormatAdapter().readFile(TEST_DATA2,
+ AppletFormatAdapter.PASTE, "FASTA");
+ al1.setDataset(null);
+ // unaligned cDNA:
+ Alignment al2 = new FormatAdapter().readFile(TEST_DATA3,
+ AppletFormatAdapter.PASTE, "FASTA");
+ al2.setDataset(null);
+
+ al1.alignAs(al2);
+ assertEquals("ACG---GCUCCA------ACT", al1.getSequenceAt(0)
+ .getSequenceAsString());
+ assertEquals("---CGT---TAACGA---AGT---", al1.getSequenceAt(1)
+ .getSequenceAsString());
+ }
}