Revert "Merge branch 'bug/JAL-3806_mappingCoversSequence' into releases/Release_2_11_...
[jalview.git] / src / jalview / util / MappingUtils.java
index cf90bf9..177c54d 100644 (file)
  */
 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;
@@ -40,13 +48,6 @@ import jalview.datamodel.Sequence;
 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.
  * 
@@ -79,7 +80,7 @@ public final class MappingUtils
       action = action.getUndoAction();
     }
     // TODO write this
-    System.err.println("MappingUtils.mapCutOrPaste not yet implemented");
+    Cache.log.error("MappingUtils.mapCutOrPaste not yet implemented");
   }
 
   /**
@@ -342,26 +343,23 @@ public final class MappingUtils
         firstUngappedPos++;
       }
 
-      /*
-       * If this sequence is only gaps in the selected range, skip it
-       */
-      if (firstUngappedPos > selectionEndRes)
-      {
-        continue;
-      }
+      boolean allGapped = (firstUngappedPos > selectionEndRes);
 
       int lastUngappedPos = selectionEndRes;
-      while (lastUngappedPos >= selectionStartRes
-              && Comparison.isGap(selected.getCharAt(lastUngappedPos)))
+      if (!allGapped)
       {
-        lastUngappedPos--;
+        while (lastUngappedPos >= selectionStartRes
+                && Comparison.isGap(selected.getCharAt(lastUngappedPos)))
+        {
+          lastUngappedPos--;
+        }
       }
 
       /*
        * Find the selected start/end residue positions in sequence
        */
-      int startResiduePos = selected.findPosition(firstUngappedPos);
-      int endResiduePos = selected.findPosition(lastUngappedPos);
+      int startResiduePos = allGapped ? 0 : selected.findPosition(firstUngappedPos);
+      int endResiduePos = allGapped ? 0 : selected.findPosition(lastUngappedPos);
 
       for (AlignedCodonFrame acf : codonFrames)
       {
@@ -375,6 +373,14 @@ public final class MappingUtils
           {
             continue;
           }
+          mappedGroup.addSequence(seq, false);
+          if (allGapped)
+          {
+            /*
+             * sequence is mapped but includes no mapped residues
+             */
+            continue;
+          }
           int mappedStartResidue = 0;
           int mappedEndResidue = 0;
           List<AlignedCodonFrame> mapping = Arrays.asList(acf);
@@ -402,7 +408,6 @@ public final class MappingUtils
           int mappedEndCol = seq.findIndex(mappedEndResidue) - 1;
           maxEndCol = maxEndCol == -1 ? mappedEndCol
                   : Math.max(maxEndCol, mappedEndCol);
-          mappedGroup.addSequence(seq, false);
           break;
         }
       }
@@ -443,20 +448,23 @@ public final class MappingUtils
     {
       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;
             }
           }
-        }
       }
     }
 
@@ -831,7 +839,7 @@ public final class MappingUtils
     {
       if (range.length % 2 != 0)
       {
-        System.err.println(
+        Cache.log.error(
                 "Error unbalance start/end ranges: " + ranges.toString());
         return 0;
       }
@@ -987,7 +995,7 @@ public final class MappingUtils
         /*
          * not coded for [start1, end1, start2, end2, ...]
          */
-        System.err.println(
+        Cache.log.error(
                 "MappingUtils.removeEndPositions doesn't handle multiple  ranges");
         return;
       }
@@ -998,7 +1006,7 @@ public final class MappingUtils
         /*
          * not coded for a reverse strand range (end < start)
          */
-        System.err.println(
+        Cache.log.error(
                 "MappingUtils.removeEndPositions doesn't handle reverse strand");
         return;
       }
@@ -1033,4 +1041,66 @@ public final class MappingUtils
     }
     return result;
   }
+
+  /**
+   * Returns the maximal start-end positions in the given (ordered) list of
+   * ranges which is overlapped by the given begin-end range, or null if there
+   * is no overlap.
+   * 
+   * <pre>
+   * Examples:
+   *   if ranges is {[4, 8], [10, 12], [16, 19]}
+   * then
+   *   findOverlap(ranges, 1, 20) == [4, 19]
+   *   findOverlap(ranges, 6, 11) == [6, 11]
+   *   findOverlap(ranges, 9, 15) == [10, 12]
+   *   findOverlap(ranges, 13, 15) == null
+   * </pre>
+   * 
+   * @param ranges
+   * @param begin
+   * @param end
+   * @return
+   */
+  protected static int[] findOverlap(List<int[]> ranges, final int begin,
+          final int end)
+  {
+    boolean foundStart = false;
+    int from = 0;
+    int to = 0;
+
+    /*
+     * traverse the ranges to find the first position (if any) >= begin,
+     * and the last position (if any) <= end
+     */
+    for (int[] range : ranges)
+    {
+      if (!foundStart)
+      {
+        if (range[0] >= begin)
+        {
+          /*
+           * first range that starts with, or follows, begin
+           */
+          foundStart = true;
+          from = Math.max(range[0], begin);
+        }
+        else if (range[1] >= begin)
+        {
+          /*
+           * first range that contains begin
+           */
+          foundStart = true;
+          from = begin;
+        }
+      }
+
+      if (range[0] <= end)
+      {
+        to = Math.min(end, range[1]);
+      }
+    }
+
+    return foundStart && to >= from ? new int[] { from, to } : null;
+  }
 }