JAL-653 (experimental) methods to resolve dummy to real sequence
[jalview.git] / src / jalview / datamodel / AlignedCodonFrame.java
index d0b2731..d24593a 100644 (file)
  */
 package jalview.datamodel;
 
-import java.util.ArrayList;
-import java.util.List;
-
 import jalview.util.MapList;
 import jalview.util.MappingUtils;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Stores mapping between the columns of a protein alignment and a DNA alignment
  * and a list of individual codon to amino acid mappings between sequences.
@@ -33,12 +33,12 @@ import jalview.util.MappingUtils;
 public class AlignedCodonFrame
 {
 
-  /*
+  /**
    * tied array of na Sequence objects.
    */
   private SequenceI[] dnaSeqs = null;
 
-  /*
+  /**
    * tied array of Mappings to protein sequence Objects and SequenceI[]
    * aaSeqs=null; MapLists where each maps from the corresponding dnaSeqs
    * element to corresponding aaSeqs element
@@ -373,7 +373,8 @@ public class AlignedCodonFrame
       if (dnaSeqs[mi] == targetDs && dnaToProt[mi].to == sourceDs)
       {
         int[] codon = dnaToProt[mi].map.locateInFrom(pos, pos);
-        if (codon != null) {
+        if (codon != null)
+        {
           return codon;
         }
       }
@@ -398,13 +399,13 @@ public class AlignedCodonFrame
       return null;
     }
     MapList ml = null;
-    char[] dnaSeq = null;
+    SequenceI dnaSeq = null;
     for (int i = 0; i < dnaToProt.length; i++)
     {
       if (dnaToProt[i].to == protein)
       {
         ml = getdnaToProt()[i];
-        dnaSeq = dnaSeqs[i].getSequence();
+        dnaSeq = dnaSeqs[i];
         break;
       }
     }
@@ -422,9 +423,10 @@ public class AlignedCodonFrame
      * Read off the mapped nucleotides (converting to position base 0)
      */
     codonPos = MappingUtils.flattenRanges(codonPos);
-    return new char[]
-    { dnaSeq[codonPos[0] - 1], dnaSeq[codonPos[1] - 1],
-        dnaSeq[codonPos[2] - 1] };
+    char[] dna = dnaSeq.getSequence();
+    int start = dnaSeq.getStart();
+    return new char[] { dna[codonPos[0] - start], dna[codonPos[1] - start],
+        dna[codonPos[2] - start] };
   }
 
   /**
@@ -444,7 +446,7 @@ public class AlignedCodonFrame
     List<SequenceI> related = new ArrayList<SequenceI>();
     SequenceI seqDs = seq.getDatasetSequence();
     seqDs = seqDs != null ? seqDs : seq;
-  
+
     for (int ds = 0; ds < dnaSeqs.length; ds++)
     {
       final Mapping mapping = dnaToProt[ds];
@@ -459,4 +461,117 @@ public class AlignedCodonFrame
     }
     return result;
   }
+
+  /**
+   * Test whether the given sequence is substitutable for one or more dummy
+   * sequences in this mapping
+   * 
+   * @param map
+   * @param seq
+   * @return
+   */
+  public boolean isRealisableWith(SequenceI seq)
+  {
+    return realiseWith(seq, false) > 0;
+  }
+
+  /**
+   * Replace any matchable mapped dummy sequences with the given real one.
+   * Returns the count of sequence mappings instantiated.
+   * 
+   * @param seq
+   * @return
+   */
+  public int realiseWith(SequenceI seq)
+  {
+    return realiseWith(seq, true);
+  }
+
+  /**
+   * Returns the number of mapped dummy sequences that could be replaced with
+   * the given real sequence.
+   * 
+   * @param seq
+   *          a dataset sequence
+   * @param doUpdate
+   *          if true, performs replacements, else only counts
+   * @return
+   */
+  protected int realiseWith(SequenceI seq, boolean doUpdate)
+  {
+    SequenceI ds = seq.getDatasetSequence() != null ? seq
+            .getDatasetSequence() : seq;
+    int count = 0;
+
+    /*
+     * check for replaceable DNA ('map from') sequences
+     */
+    for (int i = 0; i < dnaSeqs.length; i++)
+    {
+      SequenceI dna = dnaSeqs[i];
+      if (dna instanceof SequenceDummy
+              && dna.getName().equals(ds.getName()))
+      {
+        Mapping mapping = dnaToProt[i];
+        int mapStart = mapping.getMap().getFromLowest();
+        int mapEnd = mapping.getMap().getFromHighest();
+        boolean mappable = couldReplaceSequence(dna, ds, mapStart, mapEnd);
+        if (mappable)
+        {
+          count++;
+          if (doUpdate)
+          {
+            dnaSeqs[i] = ds;
+          }
+        }
+      }
+
+      /*
+       * check for replaceable protein ('map to') sequences
+       */
+      SequenceI prot = dnaToProt[i].getTo();
+      Mapping mapping = dnaToProt[i];
+      int mapStart = mapping.getMap().getToLowest();
+      int mapEnd = mapping.getMap().getToHighest();
+      boolean mappable = couldReplaceSequence(prot, ds, mapStart, mapEnd);
+      if (mappable)
+      {
+        count++;
+        if (doUpdate)
+        {
+          dnaToProt[i].setTo(ds);
+        }
+      }
+    }
+    return count;
+  }
+
+  /**
+   * Helper method to test whether a 'real' sequence could replace a 'dummy'
+   * sequence in the map. The criteria are that they have the same name, and
+   * that the mapped region overlaps the candidate sequence.
+   * 
+   * @param existing
+   * @param replacement
+   * @param mapStart
+   * @param mapEnd
+   * @return
+   */
+  protected static boolean couldReplaceSequence(SequenceI existing,
+          SequenceI replacement, int mapStart, int mapEnd)
+  {
+    if (existing instanceof SequenceDummy
+            && existing.getName().equals(replacement.getName()))
+    {
+      int start = replacement.getStart();
+      int end = replacement.getEnd();
+      boolean mappingOverlapsSequence = (mapStart >= start && mapStart <= end)
+              || (mapEnd >= start && mapEnd <= end);
+      if (mappingOverlapsSequence)
+      {
+        return true;
+      }
+    }
+    return false;
+  }
 }