*/
package jalview.analysis;
+import jalview.datamodel.AlignedCodon;
import jalview.datamodel.AlignedCodonFrame;
import jalview.datamodel.AlignmentAnnotation;
import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Mapping;
import jalview.datamodel.SearchResults;
import jalview.datamodel.Sequence;
import jalview.datamodel.SequenceI;
import jalview.util.MapList;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
+import java.util.TreeMap;
/**
* grab bag of useful alignment manipulation operations Expect these to be
/**
* Build mapping of protein to cDNA alignment. Mappings are made between
- * sequences which have the same name and compatible lengths. Has a 3-valued
- * result: either Mapped (at least one sequence mapping was created),
- * AlreadyMapped (all possible sequence mappings already exist), or NotMapped
- * (no possible sequence mappings exist).
+ * sequences which have the same name and compatible lengths. Any new mappings
+ * are added to the protein alignment. Has a 3-valued result: either Mapped
+ * (at least one sequence mapping was created), AlreadyMapped (all possible
+ * sequence mappings already exist), or NotMapped (no possible sequence
+ * mappings exist).
*
* @param proteinAlignment
* @param cdnaAlignment
alignedSeq.setSequence(newseq.toString());
return alignedSeq;
}
+
+ /**
+ * Realigns the given protein to match the alignment of the dna, using codon
+ * mappings to translate aligned codon positions to protein residues.
+ *
+ * @param protein
+ * the alignment whose sequences are realigned by this method
+ * @param dna
+ * the dna alignment whose alignment we are 'copying'
+ * @return the number of sequences that were realigned
+ */
+ public static int alignProteinAsDna(AlignmentI protein, AlignmentI dna)
+ {
+ Set<AlignedCodonFrame> mappings = protein.getCodonFrames();
+
+ /*
+ * Map will hold, for each aligned codon position e.g. [3, 5, 6], a map of
+ * {dnaSequence, {proteinSequence, codonProduct}} at that position. The
+ * comparator keeps the codon positions ordered.
+ */
+ Map<AlignedCodon, Map<SequenceI, String>> alignedCodons = new TreeMap<AlignedCodon, Map<SequenceI, String>>(
+ new CodonComparator());
+ for (SequenceI dnaSeq : dna.getSequences())
+ {
+ for (AlignedCodonFrame mapping : mappings)
+ {
+ Mapping seqMap = mapping.getMappingForSequence(dnaSeq);
+ SequenceI prot = mapping.findAlignedSequence(
+ dnaSeq.getDatasetSequence(), protein);
+ if (prot != null)
+ {
+ addCodonPositions(dnaSeq, prot, protein.getGapCharacter(),
+ seqMap, alignedCodons);
+ }
+ }
+ }
+ return alignProteinAs(protein, alignedCodons);
+ }
+
+ /**
+ * Update the aligned protein sequences to match the codon alignments given in
+ * the map.
+ *
+ * @param protein
+ * @param alignedCodons
+ * an ordered map of codon positions (columns), with sequence/peptide
+ * values present in each column
+ * @return
+ */
+ protected static int alignProteinAs(AlignmentI protein,
+ Map<AlignedCodon, Map<SequenceI, String>> alignedCodons)
+ {
+ /*
+ * Prefill aligned sequences with gaps before inserting aligned protein
+ * residues.
+ */
+ int alignedWidth = alignedCodons.size();
+ char[] gaps = new char[alignedWidth];
+ Arrays.fill(gaps, protein.getGapCharacter());
+ String allGaps = String.valueOf(gaps);
+ for (SequenceI seq : protein.getSequences())
+ {
+ seq.setSequence(allGaps);
+ }
+
+ int column = 0;
+ for (AlignedCodon codon : alignedCodons.keySet())
+ {
+ final Map<SequenceI, String> columnResidues = alignedCodons.get(codon);
+ for (Entry<SequenceI, String> entry : columnResidues
+ .entrySet())
+ {
+ // place translated codon at its column position in sequence
+ entry.getKey().getSequence()[column] = entry.getValue().charAt(0);
+ }
+ column++;
+ }
+ return 0;
+ }
+
+ /**
+ * Populate the map of aligned codons by traversing the given sequence
+ * mapping, locating the aligned positions of mapped codons, and adding those
+ * positions and their translation products to the map.
+ *
+ * @param dna
+ * the aligned sequence we are mapping from
+ * @param protein
+ * the sequence to be aligned to the codons
+ * @param gapChar
+ * the gap character in the dna sequence
+ * @param seqMap
+ * a mapping to a sequence translation
+ * @param alignedCodons
+ * the map we are building up
+ */
+ static void addCodonPositions(SequenceI dna, SequenceI protein,
+ char gapChar,
+ Mapping seqMap,
+ Map<AlignedCodon, Map<SequenceI, String>> alignedCodons)
+ {
+ Iterator<AlignedCodon> codons = seqMap.getCodonIterator(dna, gapChar);
+ while (codons.hasNext())
+ {
+ AlignedCodon codon = codons.next();
+ Map<SequenceI, String> seqProduct = alignedCodons.get(codon);
+ if (seqProduct == null)
+ {
+ seqProduct = new HashMap<SequenceI, String>();
+ alignedCodons.put(codon, seqProduct);
+ }
+ seqProduct.put(protein, codon.product);
+ }
+ }
}
package jalview.api;
+import jalview.datamodel.AlignmentI;
+
+import java.awt.Component;
+
/**
* Describes a visual container that can show two alignments.
*
* @param show
*/
// TODO need an interface for AlignFrame?
- void setComplementVisible(Object alignFrame, boolean show);
+ void setComplementVisible(Component alignFrame, boolean show);
+
+ /**
+ * Returns the alignment that is complementary to the one in the given
+ * AlignFrame, or null.
+ */
+ AlignmentI getComplement(Component af);
+
+ /**
+ * Returns the frame title for the alignment that is complementary to the one
+ * in the given AlignFrame, or null.
+ *
+ * @param af
+ * @return
+ */
+ String getComplementTitle(Component af);
}
/**
* Holds the aligned column positions (base 0) for one codon in a nucleotide
- * sequence. The object is immutable once created.
+ * sequence, and (optionally) its peptide translation. The object is immutable
+ * once created.
*
* Example: in "G-AT-C-GA" the aligned codons are (0, 2, 3) and (5, 7, 8).
*
public final int pos3;
+ public final String product;
+
public AlignedCodon(int i, int j, int k)
{
+ this(i, j, k, null);
+ }
+
+ public AlignedCodon(int i, int j, int k, String prod)
+ {
pos1 = i;
pos2 = j;
pos3 = k;
+ product = prod;
}
/**
}
/**
- * Two aligned codons are equal if all their base positions are the same.
+ * Two aligned codons are equal if all their base positions are the same. We
+ * don't care about the protein product. This test is required for correct
+ * alignment of translated gapped dna alignments (the same codon positions in
+ * different sequences occupy the same column in the translated alignment).
*/
@Override
public boolean equals(Object o)
@Override
public String toString()
{
- return "[" + pos1 + ", " + pos2 + ", " + pos3 + "]";
+ StringBuilder sb = new StringBuilder();
+ sb.append("[").append(pos1).append(", ").append(pos2).append(", ")
+ .append(pos3).append("]");
+ return sb.toString();
}
}
}
/**
+ * Returns the first mapping found which is to or from the given sequence, or
+ * null.
+ *
+ * @param seq
+ * @return
+ */
+ public Mapping getMappingForSequence(SequenceI seq)
+ {
+ if (dnaSeqs == null)
+ {
+ return null;
+ }
+ SequenceI seqDs = seq.getDatasetSequence();
+ seqDs = seqDs != null ? seqDs : seq;
+
+ for (int ds = 0; ds < dnaSeqs.length; ds++)
+ {
+ if (dnaSeqs[ds] == seqDs || dnaToProt[ds].to == seqDs)
+ {
+ return dnaToProt[ds];
+ }
+ }
+ return null;
+ }
+
+ /**
* Return the corresponding aligned or dataset aa sequence for given dna
* sequence, null if not found.
*
boolean thatIsProtein = !al.isNucleotide();
if (!thatIsProtein && !thisIsNucleotide)
{
- System.err
- .println("Alignment of protein from cDNA not yet implemented");
- return 0;
- // todo: build it - a variant of Dna.CdnaTranslate()
+ return AlignmentUtils.alignProteinAsDna(this, al);
}
char thisGapChar = this.getGapCharacter();
--- /dev/null
+package jalview.datamodel;
+
+/**
+ * An exception to indicate that less than 3 nucleotide bases are available when
+ * trying to form a codon.
+ *
+ * @author gmcarstairs
+ *
+ */
+public class IncompleteCodonException extends RuntimeException
+{
+
+}
import jalview.util.MapList;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
import java.util.Vector;
public class Mapping
{
/**
+ * An iterator that serves the aligned codon positions (with their protein
+ * products).
+ *
+ * @author gmcarstairs
+ *
+ */
+ public class AlignedCodonIterator implements Iterator<AlignedCodon>
+ {
+ /*
+ * The gap character used in the aligned sequence
+ */
+ private final char gap;
+
+ /*
+ * The characters of the aligned sequence e.g. "-cGT-ACgTG-"
+ */
+ private final char[] alignedSeq;
+
+ /*
+ * Next position (base 0) in the aligned sequence
+ */
+ private int alignedColumn = 0;
+
+ /*
+ * Count of bases up to and including alignedColumn position
+ */
+ private int alignedBases = 0;
+
+ /*
+ * [start, end] from ranges (base 1)
+ */
+ private Iterator<int[]> fromRanges;
+
+ /*
+ * [start, end] to ranges (base 1)
+ */
+ private Iterator<int[]> toRanges;
+
+ /*
+ * The current [start, end] (base 1) from range
+ */
+ private int[] currentFromRange = null;
+
+ /*
+ * The current [start, end] (base 1) to range
+ */
+ private int[] currentToRange = null;
+
+ /*
+ * The next 'from' position (base 1) to process
+ */
+ private int fromPosition = 0;
+
+ /*
+ * The next 'to' position (base 1) to process
+ */
+ private int toPosition = 0;
+
+ /**
+ * Constructor
+ *
+ * @param cs
+ * the aligned sequence characters
+ * @param gapChar
+ */
+ public AlignedCodonIterator(char[] cs, char gapChar)
+ {
+ this.alignedSeq = cs;
+ this.gap = gapChar;
+ fromRanges = map.getFromRanges().iterator();
+ toRanges = map.getToRanges().iterator();
+ if (fromRanges.hasNext())
+ {
+ currentFromRange = fromRanges.next();
+ fromPosition = currentFromRange[0];
+ }
+ if (toRanges.hasNext())
+ {
+ currentToRange = toRanges.next();
+ toPosition = currentToRange[0];
+ }
+ }
+
+ /**
+ * Returns true unless we have already traversed the whole mapping.
+ */
+ @Override
+ public boolean hasNext()
+ {
+ if (fromRanges.hasNext())
+ {
+ return true;
+ }
+ if (currentFromRange == null || fromPosition >= currentFromRange[1])
+ {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns the next codon's aligned positions, and translated value.
+ *
+ * @throws NoSuchElementException
+ * if hasNext() would have returned false
+ * @throws IncompleteCodonException
+ * if not enough mapped bases are left to make up a codon
+ */
+ @Override
+ public AlignedCodon next() throws IncompleteCodonException
+ {
+ if (!hasNext())
+ {
+ throw new NoSuchElementException();
+ }
+
+ int[] codon = getNextCodon();
+ int[] alignedCodon = getAlignedCodon(codon);
+
+ String peptide = getPeptide();
+ return new AlignedCodon(alignedCodon[0], alignedCodon[1],
+ alignedCodon[2], peptide);
+ }
+
+ /**
+ * Retrieve the translation as the 'mapped to' position in the mapped to
+ * sequence.
+ *
+ * @return
+ */
+ private String getPeptide()
+ {
+ // TODO should ideally handle toRatio other than 1 as well...
+ // i.e. code like getNextCodon()
+ if (toPosition <= currentToRange[1]) {
+ char pep = Mapping.this.to.getSequence()[toPosition - 1];
+ toPosition++;
+ return String.valueOf(pep);
+ }
+ if (!toRanges.hasNext())
+ {
+ throw new NoSuchElementException("Ran out of peptide at position "
+ + toPosition);
+ }
+ currentToRange = toRanges.next();
+ toPosition = currentToRange[0];
+ return getPeptide();
+ }
+
+ /**
+ * Get the (base 1) dataset positions for the next codon in the mapping.
+ *
+ * @throws IncompleteCodonException
+ * if less than 3 remaining bases are mapped
+ */
+ private int[] getNextCodon()
+ {
+ int[] codon = new int[3];
+ int codonbase = 0;
+
+ while (codonbase < 3)
+ {
+ if (fromPosition <= currentFromRange[1])
+ {
+ /*
+ * Add next position from the current start-end range
+ */
+ codon[codonbase++] = fromPosition++;
+ }
+ else
+ {
+ /*
+ * Move to the next range - if there is one
+ */
+ if (!fromRanges.hasNext())
+ {
+ throw new IncompleteCodonException();
+ }
+ currentFromRange = fromRanges.next();
+ fromPosition = currentFromRange[0];
+ }
+ }
+ return codon;
+ }
+
+ /**
+ * Get the aligned column positions (base 0) for the given sequence
+ * positions (base 1), by counting ungapped characters in the aligned
+ * sequence.
+ *
+ * @param codon
+ * @return
+ */
+ private int[] getAlignedCodon(int[] codon)
+ {
+ int[] aligned = new int[codon.length];
+ for (int i = 0; i < codon.length; i++)
+ {
+ aligned[i] = getAlignedColumn(codon[i]);
+ }
+ return aligned;
+ }
+
+ /**
+ * Get the aligned column position (base 0) for the given sequence position
+ * (base 1).
+ *
+ * @param sequencePos
+ * @return
+ */
+ private int getAlignedColumn(int sequencePos)
+ {
+ while (alignedBases < sequencePos
+ && alignedColumn < alignedSeq.length)
+ {
+ if (alignedSeq[alignedColumn++] != gap)
+ {
+ alignedBases++;
+ }
+ }
+ return alignedColumn - 1;
+ }
+
+ @Override
+ public void remove()
+ {
+ // ignore
+ }
+
+ }
+
+ /**
* Contains the start-end pairs mapping from the associated sequence to the
* sequence in the database coordinate system. It also takes care of step
* difference between coordinate systems.
super.finalize();
}
+ public Iterator<AlignedCodon> getCodonIterator(SequenceI seq, char gapChar)
+ {
+ return new AlignedCodonIterator(seq.getSequence(), gapChar);
+ }
+
}
{
SequenceI sequence;
- /*
+ /**
* Start position of match in sequence (base 1)
*/
int start;
- /*
+ /**
* End position (inclusive) (base 1)
*/
int end;
+ /**
+ * Constructor
+ *
+ * @param seq
+ * a sequence
+ * @param start
+ * start position of matched range (base 1)
+ * @param end
+ * end of matched range (inclusive, base 1)
+ */
public Match(SequenceI seq, int start, int end)
{
sequence = seq;
public String toString()
{
char[] chars = sequence.getSequence();
- return String.valueOf(Arrays.copyOfRange(chars, start - 1, end));
+ // convert start/end to base 0 (with bounds check)
+ final int from = Math.max(start - 1, 0);
+ final int to = Math.min(end, chars.length + 1);
+ return String.valueOf(Arrays.copyOfRange(chars, from, to));
}
}
final SequenceI[] seqs = viewport.getSelectionGroup() == null ? viewport
.getAlignment().getSequencesArray() : viewport
.getSelectionAsNewSequence();
- viewport.openSplitFrame(af, seqs);
+ viewport.openSplitFrame(af, seqs, al.getCodonFrames());
// Desktop.addInternalFrame(af, newTitle, DEFAULT_WIDTH, DEFAULT_HEIGHT);
}
}
mp = new Mapping();
jalview.util.MapList mlst = jmp.getMap();
- int r[] = mlst.getFromRanges();
- for (int s = 0; s < r.length; s += 2)
+ List<int[]> r = mlst.getFromRanges();
+ for (int[] range : r)
{
MapListFrom mfrom = new MapListFrom();
- mfrom.setStart(r[s]);
- mfrom.setEnd(r[s + 1]);
+ mfrom.setStart(range[0]);
+ mfrom.setEnd(range[1]);
mp.addMapListFrom(mfrom);
}
r = mlst.getToRanges();
- for (int s = 0; s < r.length; s += 2)
+ for (int[] range : r)
{
MapListTo mto = new MapListTo();
- mto.setStart(r[s]);
- mto.setEnd(r[s + 1]);
+ mto.setStart(range[0]);
+ mto.setEnd(range[1]);
mp.addMapListTo(mto);
}
mp.setMapFromUnit(mlst.getFromRatio());
package jalview.gui;
+import jalview.api.SplitContainerI;
import jalview.api.ViewStyleI;
import jalview.datamodel.AlignmentI;
import jalview.jbgui.GAlignFrame;
* @author gmcarstairs
*
*/
-public class SplitFrame extends GSplitFrame
+public class SplitFrame extends GSplitFrame implements SplitContainerI
{
private static final long serialVersionUID = 1L;
{
Desktop.instance.gatherViews(this);
}
+
+ /**
+ * Returns the alignment in the complementary frame to the one given.
+ */
+ @Override
+ public AlignmentI getComplement(Component alignFrame)
+ {
+ if (alignFrame == this.getTopFrame())
+ {
+ return ((AlignFrame) getBottomFrame()).viewport.getAlignment();
+ }
+ else if (alignFrame == this.getBottomFrame())
+ {
+ return ((AlignFrame) getTopFrame()).viewport.getAlignment();
+ }
+ return null;
+ }
+
+ /**
+ * Returns the title of the complementary frame to the one given.
+ */
+ @Override
+ public String getComplementTitle(Component alignFrame)
+ {
+ if (alignFrame == this.getTopFrame())
+ {
+ return ((AlignFrame) getBottomFrame()).getTitle();
+ }
+ else if (alignFrame == this.getBottomFrame())
+ {
+ return ((AlignFrame) getTopFrame()).getTitle();
+ }
+ return null;
+ }
}
+
* initialise a range type object from a set of start/end inclusive intervals
*
* @param mrt
- * @param range
+ * @param ranges
*/
- private void initRangeType(RangeType mrt, int[] range)
+ private void initRangeType(RangeType mrt, List<int[]> ranges)
{
- for (int i = 0; i < range.length; i += 2)
+ for (int[] range : ranges)
{
Seg vSeg = new Seg();
- vSeg.setStart(range[i]);
- vSeg.setEnd(range[i + 1]);
+ vSeg.setStart(range[0]);
+ vSeg.setEnd(range[1]);
mrt.addSeg(vSeg);
}
}
*/
package jalview.io.vamsas;
+import jalview.io.VamsasAppDatastore;
+import jalview.util.MessageManager;
+
+import java.util.List;
import java.util.Vector;
import uk.ac.vamsas.client.Vobject;
import uk.ac.vamsas.objects.core.Mapped;
import uk.ac.vamsas.objects.core.RangeType;
import uk.ac.vamsas.objects.core.Seg;
-import jalview.io.VamsasAppDatastore;
-import jalview.util.MessageManager;
/**
* Enhances DatastoreItem objects with additional functions to do with RangeType
* initialise a range type object from a set of start/end inclusive intervals
*
* @param mrt
- * @param range
+ * @param ranges
*/
- protected void initRangeType(RangeType mrt, int[] range)
+ protected void initRangeType(RangeType mrt, List<int[]> ranges)
{
- for (int i = 0; i < range.length; i += 2)
+ for (int[] range : ranges)
{
Seg vSeg = new Seg();
- vSeg.setStart(range[i]);
- vSeg.setEnd(range[i + 1]);
+ vSeg.setStart(range[0]);
+ vSeg.setEnd(range[1]);
vSeg.setInclusive(true);
mrt.addSeg(vSeg);
}
package jalview.jbgui;
-import jalview.api.SplitContainerI;
import jalview.util.Platform;
import java.awt.Component;
import javax.swing.JSplitPane;
import javax.swing.plaf.basic.BasicInternalFrameUI;
-public class GSplitFrame extends JInternalFrame implements SplitContainerI
+public class GSplitFrame extends JInternalFrame
{
private static final long serialVersionUID = 1L;
* Make the complement of the specified split component visible or hidden,
* adjusting the position of the split divide.
*/
- @Override
- public void setComplementVisible(Object alignFrame, boolean show)
+ public void setComplementVisible(Component alignFrame, boolean show)
{
if (alignFrame == this.topFrame)
{
}
validate();
}
-
}
}
/**
- * Returns the flattened 'from' ranges as [start1, end1, start2, end2, ...]
+ * Returns the 'from' ranges as {[start1, end1], [start2, end2], ...}
*
* @return
*/
- public int[] getFromRanges()
+ public List<int[]> getFromRanges()
{
- return getRanges(fromShifts);
+ return fromShifts;
}
/**
- * Returns the flattened 'to' ranges as [start1, end1, start2, end2, ...]
+ * Returns the 'to' ranges as {[start1, end1], [start2, end2], ...}
*
* @return
*/
- public int[] getToRanges()
+ public List<int[]> getToRanges()
{
- return getRanges(toShifts);
+ return toShifts;
}
/**
*/
public MapList(int from[], int to[], int fromRatio, int toRatio)
{
+ this.fromRatio = fromRatio;
+ this.toRatio = toRatio;
fromLowest = from[0];
fromHighest = from[1];
for (int i = 0; i < from.length; i += 2)
toShifts.add(new int[]
{ to[i], to[i + 1] });
}
- this.fromRatio = fromRatio;
- this.toRatio = toRatio;
}
/**
}
/**
+ * Constructor given ranges as lists of [start, end] positions
+ *
+ * @param fromRange
+ * @param toRange
+ * @param fromRatio
+ * @param toRatio
+ */
+ public MapList(List<int[]> fromRange, List<int[]> toRange,
+ int fromRatio, int toRatio)
+ {
+ this.fromShifts = fromRange;
+ this.toShifts = toRange;
+ this.fromRatio = fromRatio;
+ this.toRatio = toRatio;
+
+ fromLowest = Integer.MAX_VALUE;
+ fromHighest = 0;
+ for (int[] range : fromRange) {
+ fromLowest = Math.min(fromLowest, range[0]);
+ fromHighest = Math.max(fromHighest, range[1]);
+ }
+
+ toLowest = Integer.MAX_VALUE;
+ toHighest = 0;
+ for (int[] range : toRange)
+ {
+ toLowest = Math.min(toLowest, range[0]);
+ toHighest = Math.max(toHighest, range[1]);
+ }
+ }
+
+ /**
* get all mapped positions from 'from' to 'to'
*
* @return int[][] { int[] { fromStart, fromFinish, toStart, toFinish }, int
.getCodonFrames();
/*
- * Copy group name, colours, but not sequences
+ * Copy group name, name colours, but not sequences or sequence colour
+ * scheme
*/
SequenceGroup mappedGroup = new SequenceGroup(sg);
+ sg.cs = mapTo.getGlobalColourScheme();
mappedGroup.clear();
// TODO set width of mapped group
*/
protected String WsUrl = null;
- private boolean fromSplitFrame;
+ /*
+ * The AlignFrame from which the service was requested.
+ */
+ private AlignFrame alignFrame;
/**
* generic web service job/subjob poll loop
AlignmentView alview, String wsurl2)
{
super();
- // this.alignFrame = alframe;
- this.fromSplitFrame = alframe.getSplitViewContainer() != null;
+ this.alignFrame = alframe;
currentView = alframe.getCurrentView().getAlignment();
featureSettings = alframe.getFeatureRenderer().getSettings();
defGapChar = alframe.getViewport().getGapCharacter();
}
}
- protected boolean isFromSplitFrame()
+ protected AlignFrame getRequestingAlignFrame()
{
- return this.fromSplitFrame;
+ return this.alignFrame;
}
}
* @param presorder
* boolean
*/
- MsaWSThread(MsaWS server, String wsUrl, WebserviceInfo wsinfo,
+ private MsaWSThread(MsaWS server, String wsUrl, WebserviceInfo wsinfo,
jalview.gui.AlignFrame alFrame, AlignmentView alview,
String wsname, boolean subgaps, boolean presorder)
{
* If alignment was requested from one half of a SplitFrame, show in a
* SplitFrame with the other pane similarly aligned.
*/
- if (this.isFromSplitFrame())
+ AlignFrame requestedBy = getRequestingAlignFrame();
+ if (requestedBy != null && requestedBy.getSplitViewContainer() != null)
{
- // TODO will only work for protein, as it holds the codon frame mappings
- // may need this thread to hold a reference to the requesting AlignFrame
- AlignmentI complement = al.getAlignedComplement(al.getCodonFrames());
- AlignFrame af2 = new AlignFrame(complement, AlignFrame.DEFAULT_WIDTH,
- AlignFrame.DEFAULT_HEIGHT);
- String linkedTitle = MessageManager
- .getString("label.linked_view_title");
- JInternalFrame splitFrame = new SplitFrame(al.isNucleotide() ? af
- : af2, al.isNucleotide() ? af2 : af);
- Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1);
- }
- else
- {
- Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
- AlignFrame.DEFAULT_HEIGHT);
+ AlignmentI complement = requestedBy.getSplitViewContainer()
+ .getComplement(requestedBy);
+ String complementTitle = requestedBy.getSplitViewContainer()
+ .getComplementTitle(requestedBy);
+ AlignmentI copyComplement = new Alignment(complement);
+ copyComplement.alignAs(al);
+ if (copyComplement.getHeight() > 0)
+ {
+ af.setTitle(alTitle);
+ AlignFrame af2 = new AlignFrame(copyComplement,
+ AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
+ af2.setTitle(complementTitle);
+ String linkedTitle = MessageManager
+ .getString("label.linked_view_title");
+ JInternalFrame splitFrame = new SplitFrame(al.isNucleotide() ? af
+ : af2, al.isNucleotide() ? af2 : af);
+ Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1);
+ return;
+ }
}
+
+ /*
+ * Not from SplitFrame, or failed to created a complementary alignment
+ */
+ Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
+ AlignFrame.DEFAULT_HEIGHT);
}
/**
import java.io.IOException;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
assertEquals(3, mapList.getFromRatio());
assertEquals(1, mapList.getToRatio());
assertTrue(Arrays.equals(new int[]
- { 1, 9 }, mapList.getFromRanges()));
+ { 1, 9 }, mapList.getFromRanges().get(0)));
+ assertEquals(1, mapList.getFromRanges().size());
assertTrue(Arrays.equals(new int[]
- { 1, 3 }, mapList.getToRanges()));
+ { 1, 3 }, mapList.getToRanges().get(0)));
+ assertEquals(1, mapList.getToRanges().size());
/*
* Inspect mappings for Mouse protein
assertEquals(3, mapList.getFromRatio());
assertEquals(1, mapList.getToRatio());
assertTrue(Arrays.equals(new int[]
- { 1, 9 }, mapList.getFromRanges()));
+ { 1, 9 }, mapList.getFromRanges().get(0)));
+ assertEquals(1, mapList.getFromRanges().size());
assertTrue(Arrays.equals(new int[]
- { 1, 3 }, mapList.getToRanges()));
+ { 1, 3 }, mapList.getToRanges().get(0)));
+ assertEquals(1, mapList.getToRanges().size());
}
}
Mapping[] protMappings = humanMapping.getProtMappings();
// two mappings, both to cDNA with stop codon
assertEquals(2, protMappings.length);
+
MapList mapList = protMappings[0].getMap();
assertEquals(3, mapList.getFromRatio());
assertEquals(1, mapList.getToRatio());
assertTrue(Arrays.equals(new int[]
- { 1, 9 }, mapList.getFromRanges()));
+ { 1, 9 }, mapList.getFromRanges().get(0)));
+ assertEquals(1, mapList.getFromRanges().size());
assertTrue(Arrays.equals(new int[]
- { 1, 3 }, mapList.getToRanges()));
+ { 1, 3 }, mapList.getToRanges().get(0)));
+ assertEquals(1, mapList.getToRanges().size());
+
mapList = protMappings[1].getMap();
assertEquals(3, mapList.getFromRatio());
assertEquals(1, mapList.getToRatio());
assertTrue(Arrays.equals(new int[]
- { 1, 9 }, mapList.getFromRanges()));
+ { 1, 9 }, mapList.getFromRanges().get(0)));
+ assertEquals(1, mapList.getFromRanges().size());
assertTrue(Arrays.equals(new int[]
- { 1, 3 }, mapList.getToRanges()));
+ { 1, 3 }, mapList.getToRanges().get(0)));
+ assertEquals(1, mapList.getToRanges().size());
/*
* Inspect mapping for Mouse protein - should map to 1st/3rd/5th cDNA seqs
assertEquals(3, mapList.getFromRatio());
assertEquals(1, mapList.getToRatio());
assertTrue(Arrays.equals(new int[]
- { 4, 12 }, mapList.getFromRanges()));
+ { 4, 12 }, mapList.getFromRanges().get(0)));
+ assertEquals(1, mapList.getFromRanges().size());
assertTrue(Arrays.equals(new int[]
- { 1, 3 }, mapList.getToRanges()));
+ { 1, 3 }, mapList.getToRanges().get(0)));
+ assertEquals(1, mapList.getToRanges().size());
// second mapping to cDNA with stop codon
mapList = protMappings[1].getMap();
assertEquals(3, mapList.getFromRatio());
assertEquals(1, mapList.getToRatio());
assertTrue(Arrays.equals(new int[]
- { 1, 9 }, mapList.getFromRanges()));
+ { 1, 9 }, mapList.getFromRanges().get(0)));
+ assertEquals(1, mapList.getFromRanges().size());
assertTrue(Arrays.equals(new int[]
- { 1, 3 }, mapList.getToRanges()));
+ { 1, 3 }, mapList.getToRanges().get(0)));
+ assertEquals(1, mapList.getToRanges().size());
// third mapping to cDNA with start and stop codon
mapList = protMappings[2].getMap();
assertEquals(3, mapList.getFromRatio());
assertEquals(1, mapList.getToRatio());
assertTrue(Arrays.equals(new int[]
- { 4, 12 }, mapList.getFromRanges()));
+ { 4, 12 }, mapList.getFromRanges().get(0)));
+ assertEquals(1, mapList.getFromRanges().size());
assertTrue(Arrays.equals(new int[]
- { 1, 3 }, mapList.getToRanges()));
+ { 1, 3 }, mapList.getToRanges().get(0)));
+ assertEquals(1, mapList.getToRanges().size());
}
/**
assertEquals("---TGCCAT---TAC------CAG---", aligned.getSequenceAsString());
assertSame(aligned.getDatasetSequence(), dna.getDatasetSequence());
}
+
+ /**
+ * Test the method that realigns protein to match mapped codon alignment.
+ */
+ @Test
+ public void testAlignProteinAsDna()
+ {
+ // seq1 codons are [1,2,3] [4,5,6] [7,8,9] [10,11,12]
+ SequenceI dna1 = new Sequence("Seq1", "TGCCATTACCAG-");
+ // seq2 codons are [1,3,4] [5,6,7] [8,9,10] [11,12,13]
+ SequenceI dna2 = new Sequence("Seq2", "T-GCCATTACCAG");
+ // seq3 codons are [1,2,3] [4,5,7] [8,9,10] [11,12,13]
+ SequenceI dna3 = new Sequence("Seq3", "TGCCA-TTACCAG");
+ AlignmentI dna = new Alignment(new SequenceI[]
+ { dna1, dna2, dna3 });
+ dna.setDataset(null);
+
+ // protein alignment will be realigned like dna
+ SequenceI prot1 = new Sequence("Seq1", "CHYQ");
+ SequenceI prot2 = new Sequence("Seq2", "CHYQ");
+ SequenceI prot3 = new Sequence("Seq3", "CHYQ");
+ AlignmentI protein = new Alignment(new SequenceI[]
+ { prot1, prot2, prot3 });
+ protein.setDataset(null);
+
+ MapList map = new MapList(new int[]
+ { 1, 12 }, new int[]
+ { 1, 4 }, 3, 1);
+ AlignedCodonFrame acf = new AlignedCodonFrame();
+ acf.addMap(dna1.getDatasetSequence(), prot1.getDatasetSequence(), map);
+ acf.addMap(dna2.getDatasetSequence(), prot2.getDatasetSequence(), map);
+ acf.addMap(dna3.getDatasetSequence(), prot3.getDatasetSequence(), map);
+ protein.setCodonFrames(Collections.singleton(acf));
+
+ /*
+ * Translated codon order is [1,2,3] [1,3,4] [4,5,6] [4,5,7] [5,6,7] [7,8,9]
+ * [8,9,10] [10,11,12] [11,12,13]
+ */
+ AlignmentUtils.alignProteinAsDna(protein, dna);
+ assertEquals("C-H--Y-Q-", prot1.getSequenceAsString());
+ assertEquals("-C--H-Y-Q", prot2.getSequenceAsString());
+ assertEquals("C--H--Y-Q", prot3.getSequenceAsString());
+ }
+
}
--- /dev/null
+package jalview.datamodel;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+import jalview.util.MapList;
+
+import java.util.Iterator;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for Mapping$AlignedCodonIterator
+ *
+ * @author gmcarstairs
+ *
+ */
+public class AlignedCodonIteratorTest
+{
+ /**
+ * Test normal case for iterating over aligned codons.
+ */
+ @Test
+ public void testNext()
+ {
+ SequenceI from = new Sequence("Seq1", "-CgC-C-cCtAG-AtG-Gc");
+ from.createDatasetSequence();
+ SequenceI to = new Sequence("Seq1", "-PQ-R-");
+ to.createDatasetSequence();
+ MapList map = new MapList(new int[]
+ { 1, 1, 3, 4, 6, 6, 8, 10, 12, 13 }, new int[]
+ { 1, 3 }, 3, 1);
+ Mapping m = new Mapping(to.getDatasetSequence(), map);
+
+ Iterator<AlignedCodon> codons = m.getCodonIterator(from, '-');
+ AlignedCodon codon = codons.next();
+ assertEquals("[1, 3, 5]", codon.toString());
+ assertEquals("P", codon.product);
+ codon = codons.next();
+ assertEquals("[8, 10, 11]", codon.toString());
+ assertEquals("Q", codon.product);
+ codon = codons.next();
+ assertEquals("[13, 15, 17]", codon.toString());
+ assertEquals("R", codon.product);
+ assertFalse(codons.hasNext());
+ }
+
+ /**
+ * Test weird case where the mapping skips over a peptide.
+ */
+ @Test
+ public void testNext_unmappedPeptide()
+ {
+ SequenceI from = new Sequence("Seq1", "-CgC-C-cCtAG-AtG-Gc");
+ from.createDatasetSequence();
+ SequenceI to = new Sequence("Seq1", "-PQ-TR-");
+ to.createDatasetSequence();
+ MapList map = new MapList(new int[]
+ { 1, 1, 3, 4, 6, 6, 8, 10, 12, 13 }, new int[]
+ { 1, 2, 4, 4 }, 3, 1);
+ Mapping m = new Mapping(to.getDatasetSequence(), map);
+
+ Iterator<AlignedCodon> codons = m.getCodonIterator(from, '-');
+ AlignedCodon codon = codons.next();
+ assertEquals("[1, 3, 5]", codon.toString());
+ assertEquals("P", codon.product);
+ codon = codons.next();
+ assertEquals("[8, 10, 11]", codon.toString());
+ assertEquals("Q", codon.product);
+ codon = codons.next();
+ assertEquals("[13, 15, 17]", codon.toString());
+ assertEquals("R", codon.product);
+ assertFalse(codons.hasNext());
+ }
+
+ /**
+ * Test for exception thrown for an incomplete codon.
+ */
+ @Test
+ public void testNext_incompleteCodon()
+ {
+ SequenceI from = new Sequence("Seq1", "-CgC-C-cCgTt");
+ from.createDatasetSequence();
+ SequenceI to = new Sequence("Seq1", "-PQ-R-");
+ to.createDatasetSequence();
+ MapList map = new MapList(new int[]
+ { 1, 1, 3, 4, 6, 6, 8, 8 }, new int[]
+ { 1, 3 }, 3, 1);
+ Mapping m = new Mapping(to.getDatasetSequence(), map);
+
+ Iterator<AlignedCodon> codons = m.getCodonIterator(from, '-');
+ AlignedCodon codon = codons.next();
+ assertEquals("[1, 3, 5]", codon.toString());
+ assertEquals("P", codon.product);
+ try
+ {
+ codon = codons.next();
+ fail("expected exception");
+ } catch (IncompleteCodonException e)
+ {
+ // expected
+ }
+ }
+
+ /**
+ * Test normal case for iterating over aligned codons.
+ */
+ @Test
+ public void testAnother()
+ {
+ SequenceI from = new Sequence("Seq1", "TGCCATTACCAG-");
+ from.createDatasetSequence();
+ SequenceI to = new Sequence("Seq1", "CHYQ");
+ to.createDatasetSequence();
+ MapList map = new MapList(new int[]
+ { 1, 12 }, new int[]
+ { 1, 4 }, 3, 1);
+ Mapping m = new Mapping(to.getDatasetSequence(), map);
+
+ Iterator<AlignedCodon> codons = m.getCodonIterator(from, '-');
+ AlignedCodon codon = codons.next();
+ assertEquals("[0, 1, 2]", codon.toString());
+ assertEquals("C", codon.product);
+ codon = codons.next();
+ assertEquals("[3, 4, 5]", codon.toString());
+ assertEquals("H", codon.product);
+ codon = codons.next();
+ assertEquals("[6, 7, 8]", codon.toString());
+ assertEquals("Y", codon.product);
+ codon = codons.next();
+ assertEquals("[9, 10, 11]", codon.toString());
+ assertEquals("Q", codon.product);
+ assertFalse(codons.hasNext());
+ }
+}
{
SequenceI seq = new Sequence("", "abcdefghijklm");
SearchResults sr = new SearchResults();
- sr.addResult(seq, 0, 0);
+ sr.addResult(seq, 1, 1);
assertEquals("a", sr.toString());
- sr.addResult(seq, 2, 4);
+ sr.addResult(seq, 3, 5);
assertEquals("acde", sr.toString());
seq = new Sequence("", "pqrstuvwxy");
- sr.addResult(seq, 5, 6);
+ sr.addResult(seq, 6, 7);
assertEquals("acdeuv", sr.toString());
}
}
assertEquals(18, ml.getFromHighest());
assertEquals(1, ml.getToLowest());
assertEquals(6, ml.getToHighest());
- assertEquals("[2, 3, 5, 7, 9, 10, 12, 12, 14, 14, 16, 18]",
- Arrays.toString(ml.getFromRanges()));
- assertEquals("[1, 1, 3, 4, 6, 6]", Arrays.toString(ml.getToRanges()));
+ assertEquals("{[2, 3], [5, 7], [9, 10], [12, 12], [14, 14], [16, 18]}",
+ prettyPrint(ml.getFromRanges()));
+ assertEquals("{[1, 1], [3, 4], [6, 6]}", prettyPrint(ml.getToRanges()));
/*
* Also copy constructor
assertEquals(18, ml2.getFromHighest());
assertEquals(1, ml2.getToLowest());
assertEquals(6, ml2.getToHighest());
- assertEquals("[2, 3, 5, 7, 9, 10, 12, 12, 14, 14, 16, 18]",
- Arrays.toString(ml2.getFromRanges()));
- assertEquals("[1, 1, 3, 4, 6, 6]", Arrays.toString(ml2.getToRanges()));
+ assertEquals("{[2, 3], [5, 7], [9, 10], [12, 12], [14, 14], [16, 18]}",
+ prettyPrint(ml2.getFromRanges()));
+ assertEquals("{[1, 1], [3, 4], [6, 6]}", prettyPrint(ml2.getToRanges()));
+ }
+
+ /**
+ * Convert a List of {[i, j], [k, l], ...} to "[[i, j], [k, l], ...]"
+ *
+ * @param ranges
+ * @return
+ */
+ private String prettyPrint(List<int[]> ranges)
+ {
+ StringBuilder sb = new StringBuilder(ranges.size() * 5);
+ boolean first = true;
+ sb.append("{");
+ for (int[] range : ranges)
+ {
+ if (!first)
+ {
+ sb.append(", ");
+ }
+ sb.append(Arrays.toString(range));
+ first = false;
+ }
+ sb.append("}");
+ return sb.toString();
}
/**
assertEquals(ml.getFromRatio(), ml2.getToRatio());
assertEquals(ml.getToHighest(), ml2.getFromHighest());
assertEquals(ml.getFromHighest(), ml2.getToHighest());
- assertEquals(Arrays.toString(ml.getFromRanges()),
- Arrays.toString(ml2.getToRanges()));
- assertEquals(Arrays.toString(ml.getToRanges()),
- Arrays.toString(ml2.getFromRanges()));
+ assertEquals(prettyPrint(ml.getFromRanges()),
+ prettyPrint(ml2.getToRanges()));
+ assertEquals(prettyPrint(ml.getToRanges()),
+ prettyPrint(ml2.getFromRanges()));
}
}