JAL-1705 merge contiguous ranges when constructing a MapList (e.g. for
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Thu, 28 Jan 2016 09:39:21 +0000 (09:39 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Thu, 28 Jan 2016 09:39:21 +0000 (09:39 +0000)
exons in cDNA)

src/jalview/util/MapList.java
test/jalview/util/MapListTest.java

index c32447c..bf66b91 100644 (file)
@@ -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<int[]> coalesceRanges(List<int[]> ranges)
+  {
+    if (ranges == null || ranges.size() < 2) {
+      return ranges;
+    }
+
+    boolean changed = false;
+    List<int[]> merged = new ArrayList<int[]>();
+    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
index 22a3ad9..2520de0 100644 (file)
@@ -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<int[]> ranges = new ArrayList<int[]>();
+    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<int[]> 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));
+  }
 }