JAL-845 TODO comment added
[jalview.git] / src / jalview / datamodel / Alignment.java
index 9c5914f..91108bc 100755 (executable)
@@ -1594,4 +1594,116 @@ public class Alignment implements AlignmentI
   {
     return dataset;
   }
+
+  /**
+   * Answers true if the supplied alignment has the same number of sequences,
+   * and they are of equivalent length, ignoring gaps. Alignments should be of
+   * the same type (protein/nucleotide) or different types with 3:1 length
+   * scaling.
+   * 
+   * @param al
+   */
+  @Override
+  public boolean isMappableTo(AlignmentI al)
+  {
+    int thisCodonScale = this.isNucleotide() ? 1 : 3;
+    int thatCodonScale = al.isNucleotide() ? 1 : 3;
+    if (this == al || this.getHeight() != al.getHeight())
+    {
+      return false;
+    }
+
+    // TODO: match sequence ids, allow different sequence ordering?
+    // TODO: allow for stop/start codons?
+    // TODO: exclude introns
+    int i = 0;
+    for (SequenceI seq : this.getSequences())
+    {
+      final int thisSequenceDnaLength = seq.getDatasetSequence()
+              .getLength() * thisCodonScale;
+      final int thatSequenceDnaLength = al.getSequenceAt(i)
+              .getDatasetSequence().getLength()
+              * thatCodonScale;
+      if (thisSequenceDnaLength != thatSequenceDnaLength)
+      {
+        return false;
+      }
+      i++;
+    }
+    return true;
+  }
+
+  /**
+   * Align this alignment the same as the given one. If both of the same type
+   * (nucleotide/protein) then align both identically. If this is nucleotide and
+   * the other is protein, make 3 gaps for each gap in the protein sequences. If
+   * this is protein and the other is nucleotide, insert a gap for each 3 gaps
+   * (or part thereof) between nucleotide bases. The two alignments should be
+   * compatible in height and lengths, but if not, then discrepancies will be
+   * ignored with unpredictable results.
+   * 
+   * @param al
+   * @throws UnsupportedOperation
+   *           if alignment of protein from cDNA is requested (not yet
+   *           implemented)
+   */
+  @Override
+  public void alignAs(AlignmentI al)
+  {
+    boolean thisIsNucleotide = this.isNucleotide();
+    boolean thatIsProtein = !al.isNucleotide();
+    if (!thatIsProtein && !thisIsNucleotide)
+    {
+      throw new UnsupportedOperationException(
+              "Alignment of protein from cDNA not implemented");
+    }
+    char thisGapChar = this.getGapCharacter();
+    char thatGapChar = al.getGapCharacter();
+    String gap = thisIsNucleotide && thatIsProtein ? String
+            .valueOf(new char[]
+            { thisGapChar, thisGapChar, thisGapChar }) : String
+            .valueOf(thisGapChar);
+    int ratio = thisIsNucleotide && thatIsProtein ? 3 : 1;
+    int i = 0;
+    for (SequenceI seq : this.getSequences())
+    {
+      SequenceI other = al.getSequenceAt(i++);
+      if (other == null)
+      {
+        continue;
+      }
+      char[] thisDs = seq.getDatasetSequence().getSequence();
+      char[] thatDs = other.getSequence();
+      StringBuilder thisAligned = new StringBuilder(2 * thisDs.length);
+      int thisDsPosition = 0;
+      for (char thatChar : thatDs)
+      {
+        if (thatChar == thatGapChar)
+        {
+          /*
+           * Add (equivalent of) a gap
+           */
+          thisAligned.append(gap);
+        }
+        else
+        {
+          /*
+           * Add (equivalent of) a residue
+           */
+          for (int j = 0; j < ratio && thisDsPosition < thisDs.length; j++)
+          {
+            thisAligned.append(thisDs[thisDsPosition++]);
+          }
+        }
+      }
+      /*
+       * Include any 'extra' residues (there shouldn't be).
+       */
+      while (thisDsPosition < thisDs.length)
+      {
+        thisAligned.append(thisDs[thisDsPosition++]);
+      }
+      seq.setSequence(new String(thisAligned));
+    }
+  }
 }