Merge branch 'features/JAL-2360colourSchemeApplicability' into
[jalview.git] / src / jalview / util / MappingUtils.java
index 16db13a..2e30132 100644 (file)
@@ -31,8 +31,9 @@ import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentOrder;
 import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.SearchResultMatchI;
 import jalview.datamodel.SearchResults;
-import jalview.datamodel.SearchResults.Match;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
@@ -194,7 +195,7 @@ public final class MappingUtils
       /*
        * Determine all mappings from this position to mapped sequences.
        */
-      SearchResults sr = buildSearchResults(seq, seqpos, mappings);
+      SearchResultsI sr = buildSearchResults(seq, seqpos, mappings);
 
       if (!sr.isEmpty())
       {
@@ -266,10 +267,10 @@ public final class MappingUtils
    * @param seqmappings
    * @return
    */
-  public static SearchResults buildSearchResults(SequenceI seq, int index,
+  public static SearchResultsI buildSearchResults(SequenceI seq, int index,
           List<AlignedCodonFrame> seqmappings)
   {
-    SearchResults results = new SearchResults();
+    SearchResultsI results = new SearchResults();
     addSearchResults(results, seq, index, seqmappings);
     return results;
   }
@@ -283,7 +284,7 @@ public final class MappingUtils
    * @param index
    * @param seqmappings
    */
-  public static void addSearchResults(SearchResults results, SequenceI seq,
+  public static void addSearchResults(SearchResultsI results, SequenceI seq,
           int index, List<AlignedCodonFrame> seqmappings)
   {
     if (index >= seq.getStart() && index <= seq.getEnd())
@@ -319,7 +320,7 @@ public final class MappingUtils
      * Copy group name, colours etc, but not sequences or sequence colour scheme
      */
     SequenceGroup mappedGroup = new SequenceGroup(sg);
-    mappedGroup.cs = mapTo.getGlobalColourScheme();
+    mappedGroup.setColourScheme(mapTo.getGlobalColourScheme());
     mappedGroup.clear();
 
     int minStartCol = -1;
@@ -374,16 +375,17 @@ public final class MappingUtils
               /*
                * Found a sequence mapping. Locate the start/end mapped residues.
                */
-              List<AlignedCodonFrame> mapping = Arrays.asList(new AlignedCodonFrame[] { acf });
-              SearchResults sr = buildSearchResults(selected,
+              List<AlignedCodonFrame> mapping = Arrays
+                      .asList(new AlignedCodonFrame[] { acf });
+              SearchResultsI sr = buildSearchResults(selected,
                       startResiduePos, mapping);
-              for (Match m : sr.getResults())
+              for (SearchResultMatchI m : sr.getResults())
               {
                 mappedStartResidue = m.getStart();
                 mappedEndResidue = m.getEnd();
               }
               sr = buildSearchResults(selected, endResiduePos, mapping);
-              for (Match m : sr.getResults())
+              for (SearchResultMatchI m : sr.getResults())
               {
                 mappedStartResidue = Math.min(mappedStartResidue,
                         m.getStart());
@@ -555,9 +557,9 @@ public final class MappingUtils
    * @param fromGapChar
    */
   protected static void mapHiddenColumns(int[] hidden,
-          List<AlignedCodonFrame> mappings,
-          ColumnSelection mappedColumns, List<SequenceI> fromSequences,
-          List<SequenceI> toSequences, char fromGapChar)
+          List<AlignedCodonFrame> mappings, ColumnSelection mappedColumns,
+          List<SequenceI> fromSequences, List<SequenceI> toSequences,
+          char fromGapChar)
   {
     for (int col = hidden[0]; col <= hidden[1]; col++)
     {
@@ -589,9 +591,9 @@ public final class MappingUtils
    * @param fromGapChar
    */
   protected static void mapColumn(int col,
-          List<AlignedCodonFrame> mappings,
-          ColumnSelection mappedColumns, List<SequenceI> fromSequences,
-          List<SequenceI> toSequences, char fromGapChar)
+          List<AlignedCodonFrame> mappings, ColumnSelection mappedColumns,
+          List<SequenceI> fromSequences, List<SequenceI> toSequences,
+          char fromGapChar)
   {
     int[] mappedTo = findMappedColumns(col, mappings, fromSequences,
             toSequences, fromGapChar);
@@ -646,9 +648,8 @@ public final class MappingUtils
        * Get the residue position and find the mapped position.
        */
       int residuePos = fromSeq.findPosition(col);
-      SearchResults sr = buildSearchResults(fromSeq, residuePos,
-              mappings);
-      for (Match m : sr.getResults())
+      SearchResultsI sr = buildSearchResults(fromSeq, residuePos, mappings);
+      for (SearchResultMatchI m : sr.getResults())
       {
         int mappedStartResidue = m.getStart();
         int mappedEndResidue = m.getEnd();
@@ -754,74 +755,65 @@ public final class MappingUtils
   public static List<AlignedCodonFrame> findMappingsForSequence(
           SequenceI sequence, List<AlignedCodonFrame> mappings)
   {
-    List<AlignedCodonFrame> result = new ArrayList<AlignedCodonFrame>();
-    if (sequence == null || mappings == null)
-    {
-      return result;
-    }
-    for (AlignedCodonFrame mapping : mappings)
-    {
-      if (mapping.involvesSequence(sequence))
-      {
-        result.add(mapping);
-      }
-    }
-    return result;
+    return findMappingsForSequenceAndOthers(sequence, mappings, null);
   }
 
   /**
-   * Remove the last 3 mapped positions from the given ranges
+   * Returns a list of any mappings that are from or to the given (aligned or
+   * dataset) sequence, optionally limited to mappings involving one of a given
+   * list of sequences.
    * 
-   * @param ranges
-   * @param mappedLength
+   * @param sequence
+   * @param mappings
+   * @param filterList
+   * @return
    */
-  public static void unmapStopCodon(List<int[]> ranges,
-          int mappedLength)
+  public static List<AlignedCodonFrame> findMappingsForSequenceAndOthers(
+          SequenceI sequence, List<AlignedCodonFrame> mappings,
+          List<SequenceI> filterList)
   {
-    if (mappedLength < 3)
+    List<AlignedCodonFrame> result = new ArrayList<AlignedCodonFrame>();
+    if (sequence == null || mappings == null)
     {
-      return;
+      return result;
     }
-    boolean done = false;
-    int targetLength = mappedLength - 3;
-    int mapped = 0;
-    Iterator<int[]> it = ranges.iterator();
-    while (!done && it.hasNext())
+    for (AlignedCodonFrame mapping : mappings)
     {
-      int[] range = it.next();
-      int length = Math.abs(range[1] - range[0]) + 1;
-      if (mapped + length == targetLength)
-      {
-        done = true;
-      }
-      else if (mapped + length < targetLength)
-      {
-        mapped += length;
-        continue;
-      }
-      else
+      if (mapping.involvesSequence(sequence))
       {
-        /*
-         * need just a bit of this range
-         */
-        int needed = targetLength - mapped;
-        int sense = range[1] >= range[0] ? 1 : -1;
-        range[1] = range[0] + (sense * (needed - 1));
-        done = true;
+        if (filterList != null)
+        {
+          for (SequenceI otherseq : filterList)
+          {
+            SequenceI otherDataset = otherseq.getDatasetSequence();
+            if (otherseq == sequence
+                    || otherseq == sequence.getDatasetSequence()
+                    || (otherDataset != null && (otherDataset == sequence || otherDataset == sequence
+                            .getDatasetSequence())))
+            {
+              // skip sequences in subset which directly relate to sequence
+              continue;
+            }
+            if (mapping.involvesSequence(otherseq))
+            {
+              // selected a mapping contained in subselect alignment
+              result.add(mapping);
+              break;
+            }
+          }
+        }
+        else
+        {
+          result.add(mapping);
+        }
       }
     }
-    /*
-     * remove any trailing ranges
-     */
-    while (it.hasNext())
-    {
-      it.next();
-      it.remove();
-    }
+    return result;
   }
 
   /**
-   * Returns the total length of the supplied ranges
+   * Returns the total length of the supplied ranges, which may be as single
+   * [start, end] or multiple [start, end, start, end ...]
    * 
    * @param ranges
    * @return
@@ -835,7 +827,16 @@ public final class MappingUtils
     int length = 0;
     for (int[] range : ranges)
     {
-      length += Math.abs(range[1] - range[0]) + 1;
+      if (range.length % 2 != 0)
+      {
+        System.err.println("Error unbalance start/end ranges: "
+                + ranges.toString());
+        return 0;
+      }
+      for (int i = 0; i < range.length - 1; i += 2)
+      {
+        length += Math.abs(range[i + 1] - range[i]) + 1;
+      }
     }
     return length;
   }
@@ -872,4 +873,65 @@ public final class MappingUtils
     }
     return false;
   }
+
+  /**
+   * Removes a specified number of positions from the start of a ranges list.
+   * For example, could be used to adjust cds ranges to allow for an incomplete
+   * start codon. Subranges are removed completely, or their start positions
+   * adjusted, until the required number of positions has been removed from the
+   * range. Reverse strand ranges are supported. The input array is not
+   * modified.
+   * 
+   * @param removeCount
+   * @param ranges
+   *          an array of [start, end, start, end...] positions
+   * @return a new array with the first removeCount positions removed
+   */
+  public static int[] removeStartPositions(int removeCount,
+          final int[] ranges)
+  {
+    if (removeCount <= 0)
+    {
+      return ranges;
+    }
+
+    int[] copy = Arrays.copyOf(ranges, ranges.length);
+    int sxpos = -1;
+    int cdspos = 0;
+    for (int x = 0; x < copy.length && sxpos == -1; x += 2)
+    {
+      cdspos += Math.abs(copy[x + 1] - copy[x]) + 1;
+      if (removeCount < cdspos)
+      {
+        /*
+         * we have removed enough, time to finish
+         */
+        sxpos = x;
+
+        /*
+         * increment start of first exon, or decrement if reverse strand
+         */
+        if (copy[x] <= copy[x + 1])
+        {
+          copy[x] = copy[x + 1] - cdspos + removeCount + 1;
+        }
+        else
+        {
+          copy[x] = copy[x + 1] + cdspos - removeCount - 1;
+        }
+        break;
+      }
+    }
+
+    if (sxpos > 0)
+    {
+      /*
+       * we dropped at least one entire sub-range - compact the array
+       */
+      int[] nxon = new int[copy.length - sxpos];
+      System.arraycopy(copy, sxpos, nxon, 0, copy.length - sxpos);
+      return nxon;
+    }
+    return copy;
+  }
 }