JAL-653 MapList constructor merges any contiguous ranges
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Mon, 30 Nov 2015 09:10:47 +0000 (09:10 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Mon, 30 Nov 2015 09:10:47 +0000 (09:10 +0000)
src/jalview/util/MapList.java
test/jalview/util/MapListTest.java

index e456dab..8ff640b 100644 (file)
@@ -41,12 +41,12 @@ public class MapList
   /*
    * Subregions (base 1) described as { [start1, end1], [start2, end2], ...}
    */
-  private List<int[]> fromShifts = new ArrayList<int[]>();
+  private List<int[]> fromShifts;
 
   /*
    * Same format as fromShifts, for the 'mapped to' sequence
    */
-  private List<int[]> toShifts = new ArrayList<int[]>();
+  private List<int[]> toShifts;
 
   /*
    * number of steps in fromShifts to one toRatio unit
@@ -73,6 +73,15 @@ public class MapList
   private int toHighest;
 
   /**
+   * Constructor
+   */
+  public MapList()
+  {
+    fromShifts = new ArrayList<int[]>();
+    toShifts = new ArrayList<int[]>();
+  }
+
+  /**
    * Two MapList objects are equal if they are the same object, or they both
    * have populated shift ranges and all values are the same.
    */
@@ -180,7 +189,8 @@ public class MapList
   }
 
   /**
-   * Constructor.
+   * Constructor given from and to ranges as [start1, end1, start2, end2,...].
+   * If any end is equal to the next start, the ranges will be merged.
    * 
    * @param from
    *          contiguous regions as [start1, end1, start2, end2, ...]
@@ -193,25 +203,47 @@ public class MapList
    */
   public MapList(int from[], int to[], int fromRatio, int toRatio)
   {
+    this();
     this.fromRatio = fromRatio;
     this.toRatio = toRatio;
     fromLowest = from[0];
     fromHighest = from[1];
+    int added = 0;
+
     for (int i = 0; i < from.length; i += 2)
     {
       fromLowest = Math.min(fromLowest, from[i]);
       fromHighest = Math.max(fromHighest, from[i + 1]);
-
-      fromShifts.add(new int[] { from[i], from[i + 1] });
+      if (added > 0 && from[i] == fromShifts.get(added - 1)[1])
+      {
+        /*
+         * this range starts where the last ended - just extend it
+         */
+        fromShifts.get(added - 1)[1] = from[i + 1];
+      }
+      else
+      {
+        fromShifts.add(new int[] { from[i], from[i + 1] });
+        added++;
+      }
     }
 
     toLowest = to[0];
     toHighest = to[1];
+    added = 0;
     for (int i = 0; i < to.length; i += 2)
     {
       toLowest = Math.min(toLowest, to[i]);
       toHighest = Math.max(toHighest, to[i + 1]);
-      toShifts.add(new int[] { to[i], to[i + 1] });
+      if (added > 0 && to[i] == toShifts.get(added - 1)[1])
+      {
+        toShifts.get(added - 1)[1] = to[i + 1];
+      }
+      else
+      {
+        toShifts.add(new int[] { to[i], to[i + 1] });
+        added++;
+      }
     }
   }
 
@@ -222,6 +254,7 @@ public class MapList
    */
   public MapList(MapList map)
   {
+    this();
     // TODO not used - remove?
     this.fromLowest = map.fromLowest;
     this.fromHighest = map.fromHighest;
@@ -257,6 +290,7 @@ public class MapList
   public MapList(List<int[]> fromRange, List<int[]> toRange, int fromRatio,
           int toRatio)
   {
+    this();
     this.fromShifts = fromRange;
     this.toShifts = toRange;
     this.fromRatio = fromRatio;
index de3994e..e0a2d30 100644 (file)
@@ -453,6 +453,25 @@ public class MapListTest
   }
 
   /**
+   * Test constructor can merge consecutive ranges
+   */
+  @Test(groups = { "Functional" })
+  public void testConstructor_mergeRanges()
+  {
+    int[] codons = { 2, 3, 3, 7, 9, 10, 12, 12, 14, 14, 16, 17 };
+    int[] protein = { 1, 1, 1, 3, 6, 6 };
+    MapList ml = new MapList(codons, protein, 3, 1);
+    assertEquals(3, ml.getFromRatio());
+    assertEquals(2, ml.getFromLowest());
+    assertEquals(17, ml.getFromHighest());
+    assertEquals(1, ml.getToLowest());
+    assertEquals(6, ml.getToHighest());
+    assertEquals("{[2, 7], [9, 10], [12, 12], [14, 14], [16, 17]}",
+            prettyPrint(ml.getFromRanges()));
+    assertEquals("{[1, 3], [6, 6]}", prettyPrint(ml.getToRanges()));
+  }
+
+  /**
    * Convert a List of {[i, j], [k, l], ...} to "[[i, j], [k, l], ...]"
    * 
    * @param ranges