JAL-1619 refactoring / tests to support 'align linked dna as protein'
[jalview.git] / src / jalview / datamodel / Alignment.java
index b2d4f0f..cea5956 100755 (executable)
  */
 package jalview.datamodel;
 
+import jalview.analysis.AlignmentUtils;
+import jalview.io.FastaFile;
 import jalview.util.MessageManager;
 
 import java.util.ArrayList;
 import java.util.Enumeration;
+import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.Vector;
 
 /**
@@ -147,7 +151,9 @@ public class Alignment implements AlignmentI
   public SequenceI[] getSequencesArray()
   {
     if (sequences == null)
+    {
       return null;
+    }
     synchronized (sequences)
     {
       return sequences.toArray(new SequenceI[sequences.size()]);
@@ -155,6 +161,17 @@ public class Alignment implements AlignmentI
   }
 
   /**
+   * Returns a map of lists of sequences keyed by sequence name.
+   * 
+   * @return
+   */
+  @Override
+  public Map<String, List<SequenceI>> getSequencesByName()
+  {
+    return AlignmentUtils.getSequencesByName(this);
+  }
+
+  /**
    * DOCUMENT ME!
    * 
    * @param i
@@ -211,7 +228,9 @@ public class Alignment implements AlignmentI
       }
     }
     if (hiddenSequences != null)
+    {
       hiddenSequences.adjustHeightSequenceAdded();
+    }
   }
 
   /**
@@ -245,7 +264,9 @@ public class Alignment implements AlignmentI
   public void finalize()
   {
     if (getDataset() != null)
+    {
       getDataset().removeAlignmentRef();
+    }
 
     dataset = null;
     sequences = null;
@@ -754,7 +775,9 @@ public class Alignment implements AlignmentI
         continue;
       }
       if (tIndex < temp.length)
+      {
         temp[tIndex++] = annotations[i];
+      }
     }
 
     if (swap)
@@ -950,6 +973,27 @@ public class Alignment implements AlignmentI
     else if (dataset == null && data != null)
     {
       dataset = data;
+      for (int i = 0; i < getHeight(); i++)
+      {
+        SequenceI currentSeq = getSequenceAt(i);
+        SequenceI dsq = currentSeq.getDatasetSequence();
+        if (dsq == null)
+        {
+          dsq = currentSeq.createDatasetSequence();
+          dataset.addSequence(dsq);
+        }
+        else
+        {
+          while (dsq.getDatasetSequence() != null)
+          {
+            dsq = dsq.getDatasetSequence();
+          }
+          if (dataset.findIndex(dsq) == -1)
+          {
+            dataset.addSequence(dsq);
+          }
+        }
+      }
     }
     dataset.addAlignmentRef();
   }
@@ -1150,7 +1194,9 @@ public class Alignment implements AlignmentI
   public void setProperty(Object key, Object value)
   {
     if (alignmentProperties == null)
+    {
       alignmentProperties = new Hashtable();
+    }
 
     alignmentProperties.put(key, value);
   }
@@ -1159,9 +1205,13 @@ public class Alignment implements AlignmentI
   public Object getProperty(Object key)
   {
     if (alignmentProperties != null)
+    {
       return alignmentProperties.get(key);
+    }
     else
+    {
       return null;
+    }
   }
 
   @Override
@@ -1183,7 +1233,9 @@ public class Alignment implements AlignmentI
   public void addCodonFrame(AlignedCodonFrame codons)
   {
     if (codons == null)
+    {
       return;
+    }
     if (codonFrameList == null)
     {
       codonFrameList = new AlignedCodonFrame[]
@@ -1217,15 +1269,21 @@ public class Alignment implements AlignmentI
   public AlignedCodonFrame[] getCodonFrame(SequenceI seq)
   {
     if (seq == null || codonFrameList == null)
+    {
       return null;
+    }
     Vector cframes = new Vector();
     for (int f = 0; f < codonFrameList.length; f++)
     {
       if (codonFrameList[f].involvesSequence(seq))
+      {
         cframes.addElement(codonFrameList[f]);
+      }
     }
     if (cframes.size() == 0)
+    {
       return null;
+    }
     AlignedCodonFrame[] cfr = new AlignedCodonFrame[cframes.size()];
     cframes.copyInto(cfr);
     return cfr;
@@ -1252,7 +1310,9 @@ public class Alignment implements AlignmentI
   public boolean removeCodonFrame(AlignedCodonFrame codons)
   {
     if (codons == null || codonFrameList == null)
+    {
       return false;
+    }
     boolean removed = false;
     int i = 0, iSize = codonFrameList.length;
     while (i < iSize)
@@ -1444,6 +1504,27 @@ public class Alignment implements AlignmentI
     return aa;
   }
 
+  /**
+   * Returns an iterable collection of any annotations that match on given
+   * sequence ref, calcId and label (ignoring null values).
+   */
+  @Override
+  public Iterable<AlignmentAnnotation> findAnnotations(SequenceI seq,
+          String calcId, String label)
+  {
+    ArrayList<AlignmentAnnotation> aa = new ArrayList<AlignmentAnnotation>();
+    for (AlignmentAnnotation ann : getAlignmentAnnotation())
+    {
+      if (ann.getCalcId() != null && ann.getCalcId().equals(calcId)
+              && ann.sequenceRef != null && ann.sequenceRef == seq
+              && ann.label != null && ann.label.equals(label))
+      {
+        aa.add(ann);
+      }
+    }
+    return aa;
+  }
+
   @Override
   public void moveSelectedSequencesByOne(SequenceGroup sg,
           Map<SequenceI, SequenceCollectionI> map, boolean up)
@@ -1528,4 +1609,78 @@ public class Alignment implements AlignmentI
   {
     return dataset;
   }
+
+  @Override
+  public int alignAs(AlignmentI al)
+  {
+    return alignAs(al, true, true);
+  }
+
+  /**
+   * Align this alignment 'the same as' the given one. Mapped sequences only are
+   * realigned. 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. Does nothing if alignment of protein from cDNA is
+   * requested (not yet implemented).
+   * 
+   * @param al
+   */
+//  @Override
+  public int alignAs(AlignmentI al, boolean preserveMappedGaps,
+          boolean preserveUnmappedGaps)
+  {
+    // TODO should this method signature be the one in the interface?
+    int count = 0;
+    boolean thisIsNucleotide = this.isNucleotide();
+    boolean thatIsProtein = !al.isNucleotide();
+    if (!thatIsProtein && !thisIsNucleotide)
+    {
+      System.err
+              .println("Alignment of protein from cDNA not yet implemented");
+      return 0;
+      // todo: build it - a variant of Dna.CdnaTranslate()
+    }
+
+    char thisGapChar = this.getGapCharacter();
+    String gap = thisIsNucleotide && thatIsProtein ? String
+            .valueOf(new char[]
+            { thisGapChar, thisGapChar, thisGapChar }) : String
+            .valueOf(thisGapChar);
+
+    /*
+     * Get mappings from 'that' alignment's sequences to this.
+     */
+    for (SequenceI alignTo : getSequences())
+    {
+      count += AlignmentUtils.alignSequenceAs(alignTo, al, gap, preserveMappedGaps,
+              preserveUnmappedGaps) ? 1 : 0;
+    }
+    return count;
+  }
+
+  /**
+   * Returns the alignment in Fasta format. Behaviour of this method is not
+   * guaranteed between versions.
+   */
+  @Override
+  public String toString()
+  {
+    return new FastaFile().print(getSequencesArray());
+  }
+
+  /**
+   * Returns the set of distinct sequence names. No ordering is guaranteed.
+   */
+  @Override
+  public Set<String> getSequenceNames()
+  {
+    Set<String> names = new HashSet<String>();
+    for (SequenceI seq : getSequences())
+    {
+      names.add(seq.getName());
+    }
+    return names;
+  }
 }