Merge branch 'develop' into JAL-1705_trialMerge
[jalview.git] / src / jalview / util / MappingUtils.java
index 22714b8..1bbfc73 100644 (file)
@@ -522,74 +522,158 @@ public final class MappingUtils
 
     char fromGapChar = mapFrom.getAlignment().getGapCharacter();
 
-    // FIXME allow for hidden columns
-
     /*
      * For each mapped column, find the range of columns that residues in that
      * column map to.
      */
-    for (Object obj : colsel.getSelected())
+    List<SequenceI> fromSequences = mapFrom.getAlignment().getSequences();
+    List<SequenceI> toSequences = mapTo.getAlignment().getSequences();
+
+    for (Integer sel : colsel.getSelected())
     {
-      int col = ((Integer) obj).intValue();
-      int mappedToMin = Integer.MAX_VALUE;
-      int mappedToMax = Integer.MIN_VALUE;
+      mapColumn(sel.intValue(), codonFrames, mappedColumns, fromSequences,
+              toSequences, fromGapChar);
+    }
+
+    for (int[] hidden : colsel.getHiddenColumns())
+    {
+      mapHiddenColumns(hidden, codonFrames, mappedColumns, fromSequences,
+              toSequences, fromGapChar);
+    }
+    return mappedColumns;
+  }
+
+  /**
+   * Helper method that maps a [start, end] hidden column range to its mapped
+   * equivalent
+   * 
+   * @param hidden
+   * @param mappings
+   * @param mappedColumns
+   * @param fromSequences
+   * @param toSequences
+   * @param fromGapChar
+   */
+  protected static void mapHiddenColumns(int[] hidden,
+          List<AlignedCodonFrame> mappings,
+          ColumnSelection mappedColumns, List<SequenceI> fromSequences,
+          List<SequenceI> toSequences, char fromGapChar)
+  {
+    for (int col = hidden[0]; col <= hidden[1]; col++)
+    {
+      int[] mappedTo = findMappedColumns(col, mappings, fromSequences,
+              toSequences, fromGapChar);
 
       /*
-       * For each sequence in the 'from' alignment
+       * Add the range of hidden columns to the mapped selection (converting
+       * base 1 to base 0).
        */
-      for (SequenceI fromSeq : mapFrom.getAlignment().getSequences())
+      if (mappedTo != null)
       {
-        /*
-         * Ignore gaps (unmapped anyway)
-         */
-        if (fromSeq.getCharAt(col) == fromGapChar)
-        {
-          continue;
-        }
+        mappedColumns.hideColumns(mappedTo[0] - 1, mappedTo[1] - 1);
+      }
+    }
+  }
+
+  /**
+   * Helper method to map one column selection
+   * 
+   * @param col
+   *          the column number (base 0)
+   * @param mappings
+   *          the sequence mappings
+   * @param mappedColumns
+   *          the mapped column selections to add to
+   * @param fromSequences
+   * @param toSequences
+   * @param fromGapChar
+   */
+  protected static void mapColumn(int col,
+          List<AlignedCodonFrame> mappings,
+          ColumnSelection mappedColumns, List<SequenceI> fromSequences,
+          List<SequenceI> toSequences, char fromGapChar)
+  {
+    int[] mappedTo = findMappedColumns(col, mappings, fromSequences,
+            toSequences, fromGapChar);
+
+    /*
+     * Add the range of mapped columns to the mapped selection (converting
+     * base 1 to base 0). Note that this may include intron-only regions which
+     * lie between the start and end ranges of the selection.
+     */
+    if (mappedTo != null)
+    {
+      for (int i = mappedTo[0]; i <= mappedTo[1]; i++)
+      {
+        mappedColumns.addElement(i - 1);
+      }
+    }
+  }
+
+  /**
+   * Helper method to find the range of columns mapped to from one column.
+   * Returns the maximal range of columns mapped to from all sequences in the
+   * source column, or null if no mappings were found.
+   * 
+   * @param col
+   * @param mappings
+   * @param fromSequences
+   * @param toSequences
+   * @param fromGapChar
+   * @return
+   */
+  protected static int[] findMappedColumns(int col,
+          List<AlignedCodonFrame> mappings, List<SequenceI> fromSequences,
+          List<SequenceI> toSequences, char fromGapChar)
+  {
+    int[] mappedTo = new int[] { Integer.MAX_VALUE, Integer.MIN_VALUE };
+    boolean found = false;
+
+    /*
+     * For each sequence in the 'from' alignment
+     */
+    for (SequenceI fromSeq : fromSequences)
+    {
+      /*
+       * Ignore gaps (unmapped anyway)
+       */
+      if (fromSeq.getCharAt(col) == fromGapChar)
+      {
+        continue;
+      }
+
+      /*
+       * 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())
+      {
+        int mappedStartResidue = m.getStart();
+        int mappedEndResidue = m.getEnd();
+        SequenceI mappedSeq = m.getSequence();
 
         /*
-         * Get the residue position and find the mapped position.
+         * Locate the aligned sequence whose dataset is mappedSeq. TODO a
+         * datamodel that can do this efficiently.
          */
-        int residuePos = fromSeq.findPosition(col);
-        SearchResults sr = buildSearchResults(fromSeq, residuePos,
-                codonFrames);
-        for (Match m : sr.getResults())
+        for (SequenceI toSeq : toSequences)
         {
-          int mappedStartResidue = m.getStart();
-          int mappedEndResidue = m.getEnd();
-          SequenceI mappedSeq = m.getSequence();
-
-          /*
-           * Locate the aligned sequence whose dataset is mappedSeq. TODO a
-           * datamodel that can do this efficiently.
-           */
-          for (SequenceI toSeq : mapTo.getAlignment().getSequences())
+          if (toSeq.getDatasetSequence() == mappedSeq)
           {
-            if (toSeq.getDatasetSequence() == mappedSeq)
-            {
-              int mappedStartCol = toSeq.findIndex(mappedStartResidue);
-              int mappedEndCol = toSeq.findIndex(mappedEndResidue);
-              mappedToMin = Math.min(mappedToMin, mappedStartCol);
-              mappedToMax = Math.max(mappedToMax, mappedEndCol);
-              // System.out.println(fromSeq.getName() + " mapped to cols "
-              // + mappedStartCol + ":" + mappedEndCol);
-              break;
-              // note: remove break if we ever want to map one to many sequences
-            }
+            int mappedStartCol = toSeq.findIndex(mappedStartResidue);
+            int mappedEndCol = toSeq.findIndex(mappedEndResidue);
+            mappedTo[0] = Math.min(mappedTo[0], mappedStartCol);
+            mappedTo[1] = Math.max(mappedTo[1], mappedEndCol);
+            found = true;
+            break;
+            // note: remove break if we ever want to map one to many sequences
           }
         }
       }
-      /*
-       * Add the range of mapped columns to the mapped selection (converting
-       * base 1 to base 0). Note that this may include intron-only regions which
-       * lie between the start and end ranges of the selection.
-       */
-      for (int i = mappedToMin; i <= mappedToMax; i++)
-      {
-        mappedColumns.addElement(i - 1);
-      }
     }
-    return mappedColumns;
+    return found ? mappedTo : null;
   }
 
   /**