JAL-1705 align CDS and peptide products to transcripts
[jalview.git] / src / jalview / util / MapList.java
index bf66b91..34a8926 100644 (file)
@@ -322,12 +322,14 @@ public class MapList
   }
 
   /**
-   * Consolidates a list of ranges so that any contiguous ranges are merged
+   * Consolidates a list of ranges so that any contiguous ranges are merged.
+   * This assumes the ranges are already in start order (does not sort them).
    * 
    * @param ranges
-   * @return
+   * @return the same list (if unchanged), else a new merged list, leaving the
+   *         input list unchanged
    */
-  public static List<int[]> coalesceRanges(List<int[]> ranges)
+  public static List<int[]> coalesceRanges(final List<int[]> ranges)
   {
     if (ranges == null || ranges.size() < 2) {
       return ranges;
@@ -337,31 +339,56 @@ public class MapList
     List<int[]> merged = new ArrayList<int[]>();
     int[] lastRange = ranges.get(0);
     int lastDirection = lastRange[1] >= lastRange[0] ? 1 : -1;
+    lastRange = new int[] { lastRange[0], lastRange[1] };
     merged.add(lastRange);
+    boolean first = true;
     
-    for (int[] range : ranges)
+    for (final int[] range : ranges)
     {
-      if (range == lastRange)
+      if (first)
       {
+        first = false;
         continue;
       }
+      if (range[0] == lastRange[0] && range[1] == lastRange[1])
+      {
+        // drop duplicate range
+        changed = true;
+        continue;
+      }
+
+      /*
+       * drop this range if it lies within the last range
+       */
+      if ((lastDirection == 1 && range[0] >= lastRange[0]
+              && range[0] <= lastRange[1] && range[1] >= lastRange[0] && range[1] <= lastRange[1])
+              || (lastDirection == -1 && range[0] <= lastRange[0]
+                      && range[0] >= lastRange[1]
+                      && range[1] <= lastRange[0] && range[1] >= lastRange[1]))
+      {
+        changed = true;
+        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))
+      boolean sameDirection = range[1] == range[0] || direction == lastDirection;
+      boolean extending = range[0] == lastRange[1] + lastDirection;
+      boolean overlapping = (lastDirection == 1 && range[0] >= lastRange[0] && range[0] <= lastRange[1])
+              || (lastDirection == -1 && range[0] <= lastRange[0] && range[0] >= lastRange[1]);
+      if (sameDirection && (overlapping || extending))
       {
         lastRange[1] = range[1];
         changed = true;
       }
       else
       {
-        merged.add(range);
-        lastRange = range;
+        lastRange = new int[] { range[0], range[1] };
+        merged.add(lastRange);
         // careful: merging [5, 5] after [7, 6] should keep negative direction
         lastDirection = (range[1] == range[0]) ? lastDirection : direction;
       }