*/
package jalview.util;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
import jalview.analysis.AlignmentSorter;
import jalview.api.AlignViewportI;
+ import jalview.bin.Cache;
import jalview.commands.CommandI;
import jalview.commands.EditCommand;
import jalview.commands.EditCommand.Action;
import jalview.commands.EditCommand.Edit;
import jalview.commands.OrderCommand;
import jalview.datamodel.AlignedCodonFrame;
+ import jalview.datamodel.AlignedCodonFrame.SequenceToSequenceMapping;
import jalview.datamodel.AlignmentI;
import jalview.datamodel.AlignmentOrder;
import jalview.datamodel.ColumnSelection;
import jalview.datamodel.SequenceGroup;
import jalview.datamodel.SequenceI;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
/**
* Helper methods for manipulations involving sequence mappings.
*
action = action.getUndoAction();
}
// TODO write this
- System.err.println("MappingUtils.mapCutOrPaste not yet implemented");
+ Cache.log.error("MappingUtils.mapCutOrPaste not yet implemented");
}
/**
for (AlignedCodonFrame acf : codonFrames)
{
- SequenceI mappedSequence = targetIsNucleotide
- ? acf.getDnaForAaSeq(selected)
- : acf.getAaForDnaSeq(selected);
- if (mappedSequence != null)
+ for (SequenceI seq : mapTo.getAlignment().getSequences())
{
- for (SequenceI seq : mapTo.getAlignment().getSequences())
+ SequenceI peptide = targetIsNucleotide ? selected : seq;
+ SequenceI cds = targetIsNucleotide ? seq : selected;
+ SequenceToSequenceMapping s2s = acf.getCoveringMapping(cds,
+ peptide);
+ if (s2s == null)
{
- int mappedStartResidue = 0;
- int mappedEndResidue = 0;
- if (seq.getDatasetSequence() == mappedSequence)
- {
- /*
- * Found a sequence mapping. Locate the start/end mapped residues.
- */
- List<AlignedCodonFrame> mapping = Arrays
- .asList(new AlignedCodonFrame[]
- { acf });
- SearchResultsI sr = buildSearchResults(selected,
- startResiduePos, mapping);
- for (SearchResultMatchI m : sr.getResults())
- {
- mappedStartResidue = m.getStart();
- mappedEndResidue = m.getEnd();
- }
- sr = buildSearchResults(selected, endResiduePos, mapping);
- for (SearchResultMatchI m : sr.getResults())
- {
- mappedStartResidue = Math.min(mappedStartResidue,
- m.getStart());
- mappedEndResidue = Math.max(mappedEndResidue, m.getEnd());
- }
-
- /*
- * Find the mapped aligned columns, save the range. Note findIndex
- * returns a base 1 position, SequenceGroup uses base 0
- */
- int mappedStartCol = seq.findIndex(mappedStartResidue) - 1;
- minStartCol = minStartCol == -1 ? mappedStartCol
- : Math.min(minStartCol, mappedStartCol);
- int mappedEndCol = seq.findIndex(mappedEndResidue) - 1;
- maxEndCol = maxEndCol == -1 ? mappedEndCol
- : Math.max(maxEndCol, mappedEndCol);
- mappedGroup.addSequence(seq, false);
- break;
- }
+ continue;
}
+ int mappedStartResidue = 0;
+ int mappedEndResidue = 0;
+ List<AlignedCodonFrame> mapping = Arrays.asList(acf);
+ SearchResultsI sr = buildSearchResults(selected, startResiduePos,
+ mapping);
+ for (SearchResultMatchI m : sr.getResults())
+ {
+ mappedStartResidue = m.getStart();
+ mappedEndResidue = m.getEnd();
+ }
+ sr = buildSearchResults(selected, endResiduePos, mapping);
+ for (SearchResultMatchI m : sr.getResults())
+ {
+ mappedStartResidue = Math.min(mappedStartResidue, m.getStart());
+ mappedEndResidue = Math.max(mappedEndResidue, m.getEnd());
+ }
+
+ /*
+ * Find the mapped aligned columns, save the range. Note findIndex
+ * returns a base 1 position, SequenceGroup uses base 0
+ */
+ int mappedStartCol = seq.findIndex(mappedStartResidue) - 1;
+ minStartCol = minStartCol == -1 ? mappedStartCol
+ : Math.min(minStartCol, mappedStartCol);
+ int mappedEndCol = seq.findIndex(mappedEndResidue) - 1;
+ maxEndCol = maxEndCol == -1 ? mappedEndCol
+ : Math.max(maxEndCol, mappedEndCol);
+ mappedGroup.addSequence(seq, false);
+ break;
}
}
}
{
for (AlignedCodonFrame acf : mappings)
{
- SequenceI mappedSeq = mappingToNucleotide ? acf.getDnaForAaSeq(seq)
- : acf.getAaForDnaSeq(seq);
- if (mappedSeq != null)
- {
for (SequenceI seq2 : mapTo.getSequences())
{
- if (seq2.getDatasetSequence() == mappedSeq)
+ /*
+ * the corresponding peptide / CDS is the one for which there is
+ * a complete ('covering') mapping to 'seq'
+ */
+ SequenceI peptide = mappingToNucleotide ? seq2 : seq;
+ SequenceI cds = mappingToNucleotide ? seq : seq2;
+ SequenceToSequenceMapping s2s = acf.getCoveringMapping(cds,
+ peptide);
+ if (s2s != null)
{
mappedOrder.add(seq2);
j++;
break;
}
}
- }
}
}
if (colsel == null)
{
- return; // mappedColumns;
+ return;
}
char fromGapChar = mapFrom.getAlignment().getGapCharacter();
mapHiddenColumns(regions.next(), codonFrames, newHidden,
fromSequences, toSequences, fromGapChar);
}
- return; // mappedColumns;
+ return;
}
/**
*/
for (SequenceI toSeq : toSequences)
{
- if (toSeq.getDatasetSequence() == mappedSeq)
+ if (toSeq.getDatasetSequence() == mappedSeq
+ && mappedStartResidue >= toSeq.getStart()
+ && mappedEndResidue <= toSeq.getEnd())
{
int mappedStartCol = toSeq.findIndex(mappedStartResidue);
int mappedEndCol = toSeq.findIndex(mappedEndResidue);
}
}
}
+
+ /**
+ * Converts a list of {@code start-end} ranges to a single array of
+ * {@code start1, end1, start2, ... } ranges
+ *
+ * @param ranges
+ * @return
+ */
+ public static int[] rangeListToArray(List<int[]> ranges)
+ {
+ int rangeCount = ranges.size();
+ int[] result = new int[rangeCount * 2];
+ int j = 0;
+ for (int i = 0; i < rangeCount; i++)
+ {
+ int[] range = ranges.get(i);
+ result[j++] = range[0];
+ result[j++] = range[1];
+ }
+ return result;
+ }
}
import static org.testng.AssertJUnit.assertNull;
import static org.testng.AssertJUnit.assertSame;
import static org.testng.AssertJUnit.assertTrue;
+import static org.testng.AssertJUnit.fail;
import static org.testng.internal.junit.ArrayAsserts.assertArrayEquals;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.BitSet;
import java.util.List;
import org.testng.annotations.BeforeClass;
{
Cache.initLogger();
}
-
+
@BeforeClass(alwaysRun = true)
public void setUpJvOptionPane()
{
JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
}
- @Test(groups = { "Functional" })
+ @Test(groups = { "Functional" }, enabled = false)
public void testSomething()
{
MapList ml = new MapList(new int[] { 1, 5, 10, 15, 25, 20 },
assertEquals("[10, 12]", Arrays.toString(ml.locateInFrom(4, 4)));
assertEquals("[1, 6]", Arrays.toString(ml.locateInFrom(1, 2)));
assertEquals("[1, 9]", Arrays.toString(ml.locateInFrom(1, 3)));
+ // reversed range treated as if forwards:
+ assertEquals("[1, 9]", Arrays.toString(ml.locateInFrom(3, 1)));
assertEquals("[1, 12]", Arrays.toString(ml.locateInFrom(1, 4)));
assertEquals("[4, 9]", Arrays.toString(ml.locateInFrom(2, 3)));
assertEquals("[4, 12]", Arrays.toString(ml.locateInFrom(2, 4)));
assertEquals("[7, 12]", Arrays.toString(ml.locateInFrom(3, 4)));
assertEquals("[10, 12]", Arrays.toString(ml.locateInFrom(4, 4)));
+ /*
+ * partial overlap
+ */
+ assertEquals("[1, 12]", Arrays.toString(ml.locateInFrom(1, 5)));
+ assertEquals("[1, 3]", Arrays.toString(ml.locateInFrom(-1, 1)));
+
+ /*
+ * no overlap
+ */
assertNull(ml.locateInFrom(0, 0));
- assertNull(ml.locateInFrom(1, 5));
- assertNull(ml.locateInFrom(-1, 1));
+
}
/**
assertEquals("[10, 10, 12, 12, 14, 14]",
Arrays.toString(ml.locateInFrom(3, 3)));
assertEquals("[16, 18]", Arrays.toString(ml.locateInFrom(4, 4)));
+
+ /*
+ * codons at 11-16, 21-26, 31-36 mapped to peptide positions 1, 3-4, 6-8
+ */
+ ml = new MapList(new int[] { 11, 16, 21, 26, 31, 36 },
+ new int[]
+ { 1, 1, 3, 4, 6, 8 }, 3, 1);
+ assertArrayEquals(new int[] { 11, 13 }, ml.locateInFrom(1, 1));
+ assertArrayEquals(new int[] { 11, 16 }, ml.locateInFrom(1, 3));
+ assertArrayEquals(new int[] { 11, 16, 21, 23 }, ml.locateInFrom(1, 4));
+ assertArrayEquals(new int[] { 14, 16, 21, 23 }, ml.locateInFrom(3, 4));
+
+ }
+
+ @Test(groups = { "Functional" })
+ public void testLocateInFrom_reverseStrand()
+ {
+ int[] codons = new int[] { 12, 1 };
+ int[] protein = new int[] { 1, 4 };
+ MapList ml = new MapList(codons, protein, 3, 1);
+ assertEquals("[12, 10]", Arrays.toString(ml.locateInFrom(1, 1)));
+ assertEquals("[9, 4]", Arrays.toString(ml.locateInFrom(2, 3)));
}
/**
assertEquals("[1, 4]", Arrays.toString(ml.locateInTo(1, 12)));
assertEquals("[2, 2]", Arrays.toString(ml.locateInTo(4, 6)));
assertEquals("[2, 4]", Arrays.toString(ml.locateInTo(4, 12)));
+ // reverse range treated as if forwards:
+ assertEquals("[2, 4]", Arrays.toString(ml.locateInTo(12, 4)));
/*
* A part codon is treated as if a whole one.
assertEquals("[1, 4]", Arrays.toString(ml.locateInTo(3, 11)));
assertEquals("[2, 4]", Arrays.toString(ml.locateInTo(5, 11)));
+ /*
+ * partial overlap
+ */
+ assertEquals("[1, 4]", Arrays.toString(ml.locateInTo(1, 13)));
+ assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(-1, 2)));
+
+ /*
+ * no overlap
+ */
assertNull(ml.locateInTo(0, 0));
- assertNull(ml.locateInTo(1, 13));
- assertNull(ml.locateInTo(-1, 1));
}
/**
MapList ml = new MapList(codons, protein, 3, 1);
/*
- * Can't map from an unmapped position
- */
- assertNull(ml.locateInTo(1, 2));
- assertNull(ml.locateInTo(2, 4));
- assertNull(ml.locateInTo(4, 4));
-
- /*
- * Valid range or subrange of codon1 maps to protein1.
+ * Valid range or subrange of codon1 maps to protein1
*/
assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(2, 2)));
assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(3, 3)));
// codon positions 7 to 17 (part) cover proteins 2/3/4 at positions 3/4/6
assertEquals("[3, 4, 6, 6]", Arrays.toString(ml.locateInTo(7, 17)));
+ /*
+ * partial overlap
+ */
+ assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(1, 2)));
+ assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(1, 4)));
+ assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(2, 4)));
+
+ /*
+ * no overlap
+ */
+ assertNull(ml.locateInTo(4, 4));
}
/**
public void testAddMapList_sameMap()
{
MapList ml = new MapList(new int[] { 11, 15, 20, 25, 35, 30 },
- new int[] { 72, 22 }, 1, 3);
+ new int[]
+ { 72, 22 }, 1, 3);
String before = ml.toString();
ml.addMapList(ml);
assertEquals(before, ml.toString());
assertArrayEquals(new int[] { 5, 6 }, merged.get(1));
assertArrayEquals(new int[] { 12, 8 }, merged.get(2));
assertArrayEquals(new int[] { 8, 7 }, merged.get(3));
-
+
// 'subsumed' ranges are preserved
ranges.clear();
ranges.add(new int[] { 10, 30 });
- ranges.add(new int[] { 15, 25 });
+ ranges.add(new int[] { 15, 25 });
++
merged = MapList.coalesceRanges(ranges);
assertEquals(2, merged.size());
assertArrayEquals(new int[] { 10, 30 }, merged.get(0));
toRanges = compound.getToRanges();
assertEquals(2, toRanges.size());
assertArrayEquals(new int[] { 931, 901 }, toRanges.get(0));
- assertArrayEquals(new int[] { 600, 582 }, toRanges.get(1));
+ assertArrayEquals(new int[] { 600, 582}, toRanges.get(1));
/*
* 1:1 plus 1:3 should result in 1:3
assertArrayEquals(new int[] { 71, 126 }, toRanges.get(1));
/*
- * method returns null if not all regions are mapped through
+ * if not all regions are mapped through, returns what is
*/
ml1 = new MapList(new int[] { 1, 50 }, new int[] { 101, 150 }, 1, 1);
- ml2 = new MapList(new int[] { 131, 180 }, new int[] { 201, 250 }, 1, 3);
+ ml2 = new MapList(new int[] { 131, 180 }, new int[] { 201, 250 }, 1, 1);
compound = ml1.traverse(ml2);
assertNull(compound);
}
1);
assertTrue(ml.isToForwardStrand());
}
+
+ /**
+ * Test for mapping with overlapping ranges
+ */
+ @Test(groups = { "Functional" })
+ public void testLocateInFrom_withOverlap()
+ {
+ /*
+ * gene to protein...
+ */
+ int[] codons = new int[] { 1, 12, 12, 17 };
+ int[] protein = new int[] { 1, 6 };
+ MapList ml = new MapList(codons, protein, 3, 1);
+ assertEquals("[1, 3]", Arrays.toString(ml.locateInFrom(1, 1)));
+ assertEquals("[4, 6]", Arrays.toString(ml.locateInFrom(2, 2)));
+ assertEquals("[7, 9]", Arrays.toString(ml.locateInFrom(3, 3)));
+ assertEquals("[10, 12]", Arrays.toString(ml.locateInFrom(4, 4)));
+ assertEquals("[12, 14]", Arrays.toString(ml.locateInFrom(5, 5)));
+ assertEquals("[15, 17]", Arrays.toString(ml.locateInFrom(6, 6)));
+ assertEquals("[1, 6]", Arrays.toString(ml.locateInFrom(1, 2)));
+ assertEquals("[1, 9]", Arrays.toString(ml.locateInFrom(1, 3)));
+ assertEquals("[1, 12]", Arrays.toString(ml.locateInFrom(1, 4)));
+ assertEquals("[1, 12, 12, 14]", Arrays.toString(ml.locateInFrom(1, 5)));
+ assertEquals("[1, 12, 12, 17]", Arrays.toString(ml.locateInFrom(1, 6)));
+ assertEquals("[4, 9]", Arrays.toString(ml.locateInFrom(2, 3)));
+ assertEquals("[7, 12, 12, 17]", Arrays.toString(ml.locateInFrom(3, 6)));
+
+ /*
+ * partial overlap of range
+ */
+ assertEquals("[4, 12, 12, 17]", Arrays.toString(ml.locateInFrom(2, 7)));
+ assertEquals("[1, 3]", Arrays.toString(ml.locateInFrom(-1, 1)));
+
+ /*
+ * no overlap in range
+ */
+ assertNull(ml.locateInFrom(0, 0));
+
+ /*
+ * gene to CDS...from EMBL:MN908947
+ */
+ int[] gene = new int[] { 266, 13468, 13468, 21555 };
+ int[] cds = new int[] { 1, 21291 };
+ ml = new MapList(gene, cds, 1, 1);
+ assertEquals("[13468, 13468]",
+ Arrays.toString(ml.locateInFrom(13203, 13203)));
+ assertEquals("[13468, 13468]",
+ Arrays.toString(ml.locateInFrom(13204, 13204)));
+ assertEquals("[13468, 13468, 13468, 13468]",
+ Arrays.toString(ml.locateInFrom(13203, 13204)));
+ }
+
+ /**
+ * Test for mapping with overlapping ranges
+ */
+ @Test(groups = { "Functional" })
+ public void testLocateInTo_withOverlap()
+ {
+ /*
+ * gene to protein...
+ */
+ int[] codons = new int[] { 1, 12, 12, 17 };
+ int[] protein = new int[] { 1, 6 };
+ MapList ml = new MapList(codons, protein, 3, 1);
+ assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(1, 1)));
+ assertEquals("[1, 3]", Arrays.toString(ml.locateInTo(3, 8)));
+ assertEquals("[1, 4]", Arrays.toString(ml.locateInTo(2, 11)));
+ assertEquals("[1, 4]", Arrays.toString(ml.locateInTo(3, 11)));
+
+ // we want base 12 to map to both of the amino acids it codes for
+ assertEquals("[4, 5]", Arrays.toString(ml.locateInTo(12, 12)));
+ assertEquals("[4, 5]", Arrays.toString(ml.locateInTo(11, 12)));
+ assertEquals("[4, 6]", Arrays.toString(ml.locateInTo(11, 15)));
+ assertEquals("[6, 6]", Arrays.toString(ml.locateInTo(15, 17)));
+
+ /*
+ * no overlap
+ */
+ assertNull(ml.locateInTo(0, 0));
+
+ /*
+ * partial overlap
+ */
+ assertEquals("[1, 6]", Arrays.toString(ml.locateInTo(1, 18)));
+ assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(-1, 1)));
+
+ /*
+ * gene to CDS...from EMBL:MN908947
+ * the base at 13468 is used twice in transcription
+ */
+ int[] gene = new int[] { 266, 13468, 13468, 21555 };
+ int[] cds = new int[] { 1, 21291 };
+ ml = new MapList(gene, cds, 1, 1);
+ assertEquals("[13203, 13204]",
+ Arrays.toString(ml.locateInTo(13468, 13468)));
+
+ /*
+ * gene to protein
+ * the base at 13468 is in the codon for 4401N and also 4402R
+ */
+ gene = new int[] { 266, 13468, 13468, 21552 }; // stop codon excluded
+ protein = new int[] { 1, 7096 };
+ ml = new MapList(gene, protein, 3, 1);
+ assertEquals("[4401, 4402]",
+ Arrays.toString(ml.locateInTo(13468, 13468)));
+ }
+
+ @Test(groups = { "Functional" })
+ public void testTraverseToPosition()
+ {
+ List<int[]> ranges = new ArrayList<>();
+ assertNull(MapList.traverseToPosition(ranges, 0));
+
+ ranges.add(new int[] { 3, 6 });
+ assertNull(MapList.traverseToPosition(ranges, 0));
+ }
+
+ @Test(groups = { "Functional" })
+ public void testCountPositions()
+ {
+ try
+ {
+ MapList.countPositions(null, 1);
+ fail("expected exception");
+ } catch (NullPointerException e)
+ {
+ // expected
+ }
+
+ List<int[]> intervals = new ArrayList<>();
+ assertNull(MapList.countPositions(intervals, 1));
+
+ /*
+ * forward strand
+ */
+ intervals.add(new int[] { 10, 20 });
+ assertNull(MapList.countPositions(intervals, 9));
+ assertNull(MapList.countPositions(intervals, 21));
+ assertArrayEquals(new int[] { 1, 1 },
+ MapList.countPositions(intervals, 10));
+ assertArrayEquals(new int[] { 6, 1 },
+ MapList.countPositions(intervals, 15));
+ assertArrayEquals(new int[] { 11, 1 },
+ MapList.countPositions(intervals, 20));
+
+ intervals.add(new int[] { 25, 25 });
+ assertArrayEquals(new int[] { 12, 1 },
+ MapList.countPositions(intervals, 25));
+
+ // next interval repeats position 25 - which should be counted twice if
+ // traversed
+ intervals.add(new int[] { 25, 26 });
+ assertArrayEquals(new int[] { 12, 1 },
+ MapList.countPositions(intervals, 25));
+ assertArrayEquals(new int[] { 14, 1 },
+ MapList.countPositions(intervals, 26));
+
+ /*
+ * reverse strand
+ */
+ intervals.clear();
+ intervals.add(new int[] { 5, -5 });
+ assertNull(MapList.countPositions(intervals, 6));
+ assertNull(MapList.countPositions(intervals, -6));
+ assertArrayEquals(new int[] { 1, -1 },
+ MapList.countPositions(intervals, 5));
+ assertArrayEquals(new int[] { 7, -1 },
+ MapList.countPositions(intervals, -1));
+ assertArrayEquals(new int[] { 11, -1 },
+ MapList.countPositions(intervals, -5));
+
+ /*
+ * reverse then forward
+ */
+ intervals.add(new int[] { 5, 10 });
+ assertArrayEquals(new int[] { 13, 1 },
+ MapList.countPositions(intervals, 6));
+
+ /*
+ * reverse then forward then reverse
+ */
+ intervals.add(new int[] { -10, -20 });
+ assertArrayEquals(new int[] { 20, -1 },
+ MapList.countPositions(intervals, -12));
+
+ /*
+ * an interval [x, x] is treated as forward
+ */
+ intervals.add(new int[] { 30, 30 });
+ assertArrayEquals(new int[] { 29, 1 },
+ MapList.countPositions(intervals, 30));
+
+ /*
+ * it is the first matched occurrence that is returned
+ */
+ intervals.clear();
+ intervals.add(new int[] { 1, 2 });
+ intervals.add(new int[] { 2, 3 });
+ assertArrayEquals(new int[] { 2, 1 },
+ MapList.countPositions(intervals, 2));
+ intervals.add(new int[] { -1, -2 });
+ intervals.add(new int[] { -2, -3 });
+ assertArrayEquals(new int[] { 6, -1 },
+ MapList.countPositions(intervals, -2));
+ }
+
+ /**
+ * Tests for helper method that adds any overlap (plus offset) to a set of
+ * overlaps
+ */
+ @Test(groups = { "Functional" })
+ public void testAddOffsetPositions()
+ {
+ List<int[]> mapped = new ArrayList<>();
+ int[] range = new int[] {10, 20};
+ BitSet offsets = new BitSet();
+
+ MapList.addOffsetPositions(mapped, 0, range, offsets);
+ assertTrue(mapped.isEmpty()); // nothing marked for overlap
+
+ offsets.set(11);
+ MapList.addOffsetPositions(mapped, 0, range, offsets);
+ assertTrue(mapped.isEmpty()); // no offset 11 in range
+
+ offsets.set(4, 6); // this sets bits 4 and 5
+ MapList.addOffsetPositions(mapped, 0, range, offsets);
+ assertEquals(1, mapped.size());
+ assertArrayEquals(new int[] { 14, 15 }, mapped.get(0));
+
+ mapped.clear();
+ offsets.set(10);
+ MapList.addOffsetPositions(mapped, 0, range, offsets);
+ assertEquals(2, mapped.size());
+ assertArrayEquals(new int[] { 14, 15 }, mapped.get(0));
+ assertArrayEquals(new int[] { 20, 20 }, mapped.get(1));
+
+ /*
+ * reverse range
+ */
+ range = new int[] { 20, 10 };
+ mapped.clear();
+ offsets.clear();
+ MapList.addOffsetPositions(mapped, 0, range, offsets);
+ assertTrue(mapped.isEmpty()); // nothing marked for overlap
+ offsets.set(11);
+ MapList.addOffsetPositions(mapped, 0, range, offsets);
+ assertTrue(mapped.isEmpty()); // no offset 11 in range
+ offsets.set(0);
+ offsets.set(10);
+ offsets.set(6, 8); // sets bits 6 and 7
+ MapList.addOffsetPositions(mapped, 0, range, offsets);
+ assertEquals(3, mapped.size());
+ assertArrayEquals(new int[] { 20, 20 }, mapped.get(0));
+ assertArrayEquals(new int[] { 14, 13 }, mapped.get(1));
+ assertArrayEquals(new int[] { 10, 10 }, mapped.get(2));
+ }
+
+ @Test(groups = { "Functional" })
+ public void testGetPositionsForOffsets()
+ {
+ List<int[]> ranges = new ArrayList<>();
+ BitSet offsets = new BitSet();
+ List<int[]> mapped = MapList.getPositionsForOffsets(ranges, offsets);
+ assertTrue(mapped.isEmpty()); // no ranges and no offsets!
+
+ offsets.set(5, 1000);
+ mapped = MapList.getPositionsForOffsets(ranges, offsets);
+ assertTrue(mapped.isEmpty()); // no ranges
+
+ /*
+ * one range with overlap of offsets
+ */
+ ranges.add(new int[] {15, 25});
+ mapped = MapList.getPositionsForOffsets(ranges, offsets);
+ assertEquals(1, mapped.size());
+ assertArrayEquals(new int[] {20, 25}, mapped.get(0));
+
+ /*
+ * two ranges
+ */
+ ranges.add(new int[] {300, 320});
+ mapped = MapList.getPositionsForOffsets(ranges, offsets);
+ assertEquals(2, mapped.size());
+ assertArrayEquals(new int[] {20, 25}, mapped.get(0));
+ assertArrayEquals(new int[] {300, 320}, mapped.get(1));
+
+ /*
+ * boundary case - right end of first range overlaps
+ */
+ offsets.clear();
+ offsets.set(10);
+ mapped = MapList.getPositionsForOffsets(ranges, offsets);
+ assertEquals(1, mapped.size());
+ assertArrayEquals(new int[] {25, 25}, mapped.get(0));
+
+ /*
+ * boundary case - left end of second range overlaps
+ */
+ offsets.set(11);
+ mapped = MapList.getPositionsForOffsets(ranges, offsets);
+ assertEquals(2, mapped.size());
+ assertArrayEquals(new int[] {25, 25}, mapped.get(0));
+ assertArrayEquals(new int[] {300, 300}, mapped.get(1));
+
+ /*
+ * offsets into a circular range are reported in
+ * the order in which they are traversed
+ */
+ ranges.clear();
+ ranges.add(new int[] {100, 150});
+ ranges.add(new int[] {60, 80});
+ offsets.clear();
+ offsets.set(45, 55); // sets bits 45 to 54
+ mapped = MapList.getPositionsForOffsets(ranges, offsets);
+ assertEquals(2, mapped.size());
+ assertArrayEquals(new int[] {145, 150}, mapped.get(0)); // offsets 45-50
+ assertArrayEquals(new int[] {60, 63}, mapped.get(1)); // offsets 51-54
+
+ /*
+ * reverse range overlap is reported with start < end
+ */
+ ranges.clear();
+ ranges.add(new int[] {4321, 4000});
+ offsets.clear();
+ offsets.set(20, 22); // sets bits 20 and 21
+ offsets.set(30);
+ mapped = MapList.getPositionsForOffsets(ranges, offsets);
+ assertEquals(2, mapped.size());
+ assertArrayEquals(new int[] {4301, 4300}, mapped.get(0));
+ assertArrayEquals(new int[] {4291, 4291}, mapped.get(1));
+ }
+
+ @Test(groups = { "Functional" })
+ public void testGetMappedOffsetsForPositions()
+ {
+ /*
+ * start by verifying the examples in the method's Javadoc!
+ */
+ List<int[]> ranges = new ArrayList<>();
+ ranges.add(new int[] {10, 20});
+ ranges.add(new int[] {31, 40});
+ BitSet overlaps = MapList.getMappedOffsetsForPositions(1, 9, ranges, 1, 1);
+ assertTrue(overlaps.isEmpty());
+ overlaps = MapList.getMappedOffsetsForPositions(1, 11, ranges, 1, 1);
+ assertEquals(2, overlaps.cardinality());
+ assertTrue(overlaps.get(0));
+ assertTrue(overlaps.get(1));
+ overlaps = MapList.getMappedOffsetsForPositions(15, 35, ranges, 1, 1);
+ assertEquals(11, overlaps.cardinality());
+ for (int i = 5 ; i <= 11 ; i++)
+ {
+ assertTrue(overlaps.get(i));
+ }
+
+ ranges.clear();
+ ranges.add(new int[] {1, 200});
+ overlaps = MapList.getMappedOffsetsForPositions(9, 9, ranges, 1, 3);
+ assertEquals(3, overlaps.cardinality());
+ assertTrue(overlaps.get(24));
+ assertTrue(overlaps.get(25));
+ assertTrue(overlaps.get(26));
+
+ ranges.clear();
+ ranges.add(new int[] {101, 150});
+ ranges.add(new int[] {171, 180});
+ overlaps = MapList.getMappedOffsetsForPositions(101, 102, ranges, 3, 1);
+ assertEquals(1, overlaps.cardinality());
+ assertTrue(overlaps.get(0));
+ overlaps = MapList.getMappedOffsetsForPositions(150, 171, ranges, 3, 1);
+ assertEquals(1, overlaps.cardinality());
+ assertTrue(overlaps.get(16));
+
+ ranges.clear();
+ ranges.add(new int[] {101, 150});
+ ranges.add(new int[] {21, 30});
+ overlaps = MapList.getMappedOffsetsForPositions(24, 40, ranges, 3, 1);
+ assertEquals(3, overlaps.cardinality());
+ assertTrue(overlaps.get(17));
+ assertTrue(overlaps.get(18));
+ assertTrue(overlaps.get(19));
+
+ /*
+ * reverse range 1:1 (e.g. reverse strand gene to transcript)
+ */
+ ranges.clear();
+ ranges.add(new int[] {20, 10});
+ overlaps = MapList.getMappedOffsetsForPositions(12, 13, ranges, 1, 1);
+ assertEquals(2, overlaps.cardinality());
+ assertTrue(overlaps.get(7));
+ assertTrue(overlaps.get(8));
+
+ /*
+ * reverse range 3:1 (e.g. reverse strand gene to peptide)
+ * from EMBL:J03321 to P0CE20
+ */
+ ranges.clear();
+ ranges.add(new int[] {1480, 488});
+ overlaps = MapList.getMappedOffsetsForPositions(1460, 1460, ranges, 3, 1);
+ // 1460 is the end of the 7th codon
+ assertEquals(1, overlaps.cardinality());
+ assertTrue(overlaps.get(6));
+ // add one base (part codon)
+ overlaps = MapList.getMappedOffsetsForPositions(1459, 1460, ranges, 3, 1);
+ assertEquals(2, overlaps.cardinality());
+ assertTrue(overlaps.get(6));
+ assertTrue(overlaps.get(7));
+ // add second base (part codon)
+ overlaps = MapList.getMappedOffsetsForPositions(1458, 1460, ranges, 3, 1);
+ assertEquals(2, overlaps.cardinality());
+ assertTrue(overlaps.get(6));
+ assertTrue(overlaps.get(7));
+ // add third base (whole codon)
+ overlaps = MapList.getMappedOffsetsForPositions(1457, 1460, ranges, 3, 1);
+ assertEquals(2, overlaps.cardinality());
+ assertTrue(overlaps.get(6));
+ assertTrue(overlaps.get(7));
+ // add one more base (part codon)
+ overlaps = MapList.getMappedOffsetsForPositions(1456, 1460, ranges, 3, 1);
+ assertEquals(3, overlaps.cardinality());
+ assertTrue(overlaps.get(6));
+ assertTrue(overlaps.get(7));
+ assertTrue(overlaps.get(8));
+ }
}
import static org.testng.AssertJUnit.assertFalse;
import static org.testng.AssertJUnit.assertSame;
import static org.testng.AssertJUnit.assertTrue;
+import static org.testng.AssertJUnit.fail;
+
+import java.awt.Color;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+ import java.awt.Color;
+ import java.io.IOException;
+ import java.util.ArrayList;
+ import java.util.Arrays;
+ import java.util.Iterator;
+ import java.util.List;
+
+ import org.testng.annotations.BeforeClass;
+ import org.testng.annotations.Test;
+
import jalview.api.AlignViewportI;
import jalview.bin.Cache;
import jalview.commands.EditCommand;
{
Cache.initLogger();
}
-
@BeforeClass(alwaysRun = true)
public void setUpJvOptionPane()
protein.setCodonFrames(acfList);
/*
- * Select Seq1 and Seq3 in the protein (startRes=endRes=0)
+ * Select Seq1 and Seq3 in the protein
*/
SequenceGroup sg = new SequenceGroup();
sg.setColourText(true);
sg.setOutlineColour(Color.LIGHT_GRAY);
sg.addSequence(protein.getSequenceAt(0), false);
sg.addSequence(protein.getSequenceAt(2), false);
+ sg.setEndRes(protein.getWidth() - 1);
/*
* Verify the mapped sequence group in dna
assertSame(cdna.getSequenceAt(0), mappedGroup.getSequences().get(0));
assertSame(cdna.getSequenceAt(2), mappedGroup.getSequences().get(1));
assertEquals(0, mappedGroup.getStartRes());
- assertEquals(2, mappedGroup.getEndRes());
+ assertEquals(2, mappedGroup.getEndRes()); // 3 columns (1 codon)
/*
* Verify mapping sequence group from dna to protein
assertEquals(9, ranges.get(0)[1]);
}
+ @Test(groups = "Functional")
+ public void testListToArray()
+ {
+ List<int[]> ranges = new ArrayList<>();
+
+ int[] result = MappingUtils.rangeListToArray(ranges);
+ assertEquals(result.length, 0);
+ ranges.add(new int[] { 24, 12 });
+ result = MappingUtils.rangeListToArray(ranges);
+ assertEquals(result.length, 2);
+ assertEquals(result[0], 24);
+ assertEquals(result[1], 12);
+ ranges.add(new int[] { -7, 30 });
+ result = MappingUtils.rangeListToArray(ranges);
+ assertEquals(result.length, 4);
+ assertEquals(result[0], 24);
+ assertEquals(result[1], 12);
+ assertEquals(result[2], -7);
+ assertEquals(result[3], 30);
+ try
+ {
+ MappingUtils.rangeListToArray(null);
+ fail("Expected exception");
+ } catch (NullPointerException e)
+ {
+ // expected
+ }
+ }
++
+ /**
+ * Test mapping a sequence group where sequences in and outside the group
+ * share a dataset sequence (e.g. alternative CDS for the same gene)
+ * <p>
+ * This scenario doesn't arise after JAL-3763 changes, but test left as still valid
+ * @throws IOException
+ */
+ @Test(groups = { "Functional" })
+ public void testMapSequenceGroup_sharedDataset() throws IOException
+ {
+ /*
+ * Set up dna and protein Seq1/2/3 with mappings (held on the protein
+ * viewport). CDS sequences share the same 'gene' dataset sequence.
+ */
+ SequenceI dna = new Sequence("dna", "aaatttgggcccaaatttgggccc");
+ SequenceI cds1 = new Sequence("cds1/1-6", "aaattt");
+ SequenceI cds2 = new Sequence("cds1/4-9", "tttggg");
+ SequenceI cds3 = new Sequence("cds1/19-24", "gggccc");
+
+ cds1.setDatasetSequence(dna);
+ cds2.setDatasetSequence(dna);
+ cds3.setDatasetSequence(dna);
+
+ SequenceI pep1 = new Sequence("pep1", "KF");
+ SequenceI pep2 = new Sequence("pep2", "FG");
+ SequenceI pep3 = new Sequence("pep3", "GP");
+ pep1.createDatasetSequence();
+ pep2.createDatasetSequence();
+ pep3.createDatasetSequence();
+
+ /*
+ * add mappings from coding positions of dna to respective peptides
+ */
+ AlignedCodonFrame acf = new AlignedCodonFrame();
+ acf.addMap(dna, pep1,
+ new MapList(new int[]
+ { 1, 6 }, new int[] { 1, 2 }, 3, 1));
+ acf.addMap(dna, pep2,
+ new MapList(new int[]
+ { 4, 9 }, new int[] { 1, 2 }, 3, 1));
+ acf.addMap(dna, pep3,
+ new MapList(new int[]
+ { 19, 24 }, new int[] { 1, 2 }, 3, 1));
+
+ List<AlignedCodonFrame> acfList = Arrays
+ .asList(new AlignedCodonFrame[]
+ { acf });
+
+ AlignmentI cdna = new Alignment(new SequenceI[] { cds1, cds2, cds3 });
+ AlignmentI protein = new Alignment(
+ new SequenceI[]
+ { pep1, pep2, pep3 });
+ AlignViewportI cdnaView = new AlignViewport(cdna);
+ AlignViewportI peptideView = new AlignViewport(protein);
+ protein.setCodonFrames(acfList);
+
+ /*
+ * Select pep1 and pep3 in the protein alignment
+ */
+ SequenceGroup sg = new SequenceGroup();
+ sg.setColourText(true);
+ sg.setIdColour(Color.GREEN);
+ sg.setOutlineColour(Color.LIGHT_GRAY);
+ sg.addSequence(pep1, false);
+ sg.addSequence(pep3, false);
+ sg.setEndRes(protein.getWidth() - 1);
+
+ /*
+ * Verify the mapped sequence group in dna is cds1 and cds3
+ */
+ SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(sg,
+ peptideView, cdnaView);
+ assertTrue(mappedGroup.getColourText());
+ assertSame(sg.getIdColour(), mappedGroup.getIdColour());
+ assertSame(sg.getOutlineColour(), mappedGroup.getOutlineColour());
+ assertEquals(2, mappedGroup.getSequences().size());
+ assertSame(cds1, mappedGroup.getSequences().get(0));
+ assertSame(cds3, mappedGroup.getSequences().get(1));
+ // columns 1-6 selected (0-5 base zero)
+ assertEquals(0, mappedGroup.getStartRes());
+ assertEquals(5, mappedGroup.getEndRes());
+
+ /*
+ * Select mapping sequence group from dna to protein
+ */
+ sg.clear();
+ sg.addSequence(cds2, false);
+ sg.addSequence(cds1, false);
+ sg.setStartRes(0);
+ sg.setEndRes(cdna.getWidth() - 1);
+ mappedGroup = MappingUtils.mapSequenceGroup(sg, cdnaView, peptideView);
+ assertTrue(mappedGroup.getColourText());
+ assertSame(sg.getIdColour(), mappedGroup.getIdColour());
+ assertSame(sg.getOutlineColour(), mappedGroup.getOutlineColour());
+ assertEquals(2, mappedGroup.getSequences().size());
+ assertSame(protein.getSequenceAt(1), mappedGroup.getSequences().get(0));
+ assertSame(protein.getSequenceAt(0), mappedGroup.getSequences().get(1));
+ assertEquals(0, mappedGroup.getStartRes());
+ assertEquals(1, mappedGroup.getEndRes()); // two columns
+ }
}