From 2ad6ae4687351167ea0f706a2fe8574f7460104c Mon Sep 17 00:00:00 2001 From: gmungoc Date: Thu, 28 Jan 2016 09:39:21 +0000 Subject: [PATCH] JAL-1705 merge contiguous ranges when constructing a MapList (e.g. for exons in cDNA) --- src/jalview/util/MapList.java | 51 ++++++++++++++++++++++++++++++++ test/jalview/util/MapListTest.java | 57 ++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) diff --git a/src/jalview/util/MapList.java b/src/jalview/util/MapList.java index c32447c..bf66b91 100644 --- a/src/jalview/util/MapList.java +++ b/src/jalview/util/MapList.java @@ -297,6 +297,8 @@ public class MapList int toRatio) { this(); + fromRange = coalesceRanges(fromRange); + toRange = coalesceRanges(toRange); this.fromShifts = fromRange; this.toShifts = toRange; this.fromRatio = fromRatio; @@ -320,6 +322,55 @@ public class MapList } /** + * Consolidates a list of ranges so that any contiguous ranges are merged + * + * @param ranges + * @return + */ + public static List coalesceRanges(List ranges) + { + if (ranges == null || ranges.size() < 2) { + return ranges; + } + + boolean changed = false; + List merged = new ArrayList(); + int[] lastRange = ranges.get(0); + int lastDirection = lastRange[1] >= lastRange[0] ? 1 : -1; + merged.add(lastRange); + + for (int[] range : ranges) + { + if (range == lastRange) + { + continue; + } + int direction = range[1] >= range[0] ? 1 : -1; + + /* + * if next range is in the same direction as last and contiguous, + * just update the end position of the last range + */ + if ((range[1] == range[0] || direction == lastDirection) + && (range[0] == lastRange[1] || range[0] == lastRange[1] + + lastDirection)) + { + lastRange[1] = range[1]; + changed = true; + } + else + { + merged.add(range); + lastRange = range; + // careful: merging [5, 5] after [7, 6] should keep negative direction + lastDirection = (range[1] == range[0]) ? lastDirection : direction; + } + } + + return changed ? merged : ranges; + } + + /** * get all mapped positions from 'from' to 'to' * * @return int[][] { int[] { fromStart, fromFinish, toStart, toFinish }, int diff --git a/test/jalview/util/MapListTest.java b/test/jalview/util/MapListTest.java index 22a3ad9..2520de0 100644 --- a/test/jalview/util/MapListTest.java +++ b/test/jalview/util/MapListTest.java @@ -25,6 +25,7 @@ import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertSame; import static org.testng.AssertJUnit.assertTrue; +import static org.testng.internal.junit.ArrayAsserts.assertArrayEquals; import java.util.ArrayList; import java.util.Arrays; @@ -666,4 +667,60 @@ public class MapListTest 1); assertTrue(ml.isFromForwardStrand()); } + + /** + * Test the method that merges a list of ranges where possible + */ + @Test(groups = { "Functional" }) + public void testCoalesceRanges() + { + assertNull(MapList.coalesceRanges(null)); + List ranges = new ArrayList(); + assertSame(ranges, MapList.coalesceRanges(ranges)); + ranges.add(new int[] { 1, 3 }); + assertSame(ranges, MapList.coalesceRanges(ranges)); + + // add non-contiguous range: + ranges.add(new int[] { 5, 6 }); + assertSame(ranges, MapList.coalesceRanges(ranges)); + + // 'contiguous' range in opposite direction is not merged: + ranges.add(new int[] { 7, 6 }); + assertSame(ranges, MapList.coalesceRanges(ranges)); + + // merging in forward direction: + ranges.clear(); + ranges.add(new int[] { 1, 3 }); + ranges.add(new int[] { 4, 5 }); + ranges.add(new int[] { 5, 5 }); + ranges.add(new int[] { 5, 7 }); + List merged = MapList.coalesceRanges(ranges); + assertEquals(1, merged.size()); + assertArrayEquals(new int[] { 1, 7 }, merged.get(0)); + + // merging in reverse direction: + ranges.clear(); + ranges.add(new int[] { 7, 5 }); + ranges.add(new int[] { 5, 4 }); + ranges.add(new int[] { 4, 4 }); + ranges.add(new int[] { 3, 1 }); + merged = MapList.coalesceRanges(ranges); + assertEquals(1, merged.size()); + assertArrayEquals(new int[] { 7, 1 }, merged.get(0)); + + // merging with switches of direction: + ranges.clear(); + ranges.add(new int[] { 1, 3 }); + ranges.add(new int[] { 4, 5 }); + ranges.add(new int[] { 5, 5 }); + ranges.add(new int[] { 6, 6 }); + ranges.add(new int[] { 12, 10 }); + ranges.add(new int[] { 9, 8 }); + ranges.add(new int[] { 8, 8 }); + ranges.add(new int[] { 7, 7 }); + merged = MapList.coalesceRanges(ranges); + assertEquals(2, merged.size()); + assertArrayEquals(new int[] { 1, 6 }, merged.get(0)); + assertArrayEquals(new int[] { 12, 7 }, merged.get(1)); + } } -- 1.7.10.2