JAL-2054 Renamed getStartEnd in AlignFrame to getVisibleStartAndEndIndex, and refacto...
[jalview.git] / src / jalview / datamodel / Alignment.java
index 7ea9985..a9b0d53 100755 (executable)
@@ -22,13 +22,14 @@ package jalview.datamodel;
 
 import jalview.analysis.AlignmentUtils;
 import jalview.io.FastaFile;
+import jalview.util.Comparison;
 import jalview.util.MessageManager;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.Hashtable;
-import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -47,8 +48,7 @@ public class Alignment implements AlignmentI
 
   protected List<SequenceI> sequences;
 
-  protected List<SequenceGroup> groups = java.util.Collections
-          .synchronizedList(new ArrayList<SequenceGroup>());
+  protected List<SequenceGroup> groups;
 
   protected char gapCharacter = '-';
 
@@ -60,20 +60,21 @@ public class Alignment implements AlignmentI
 
   public boolean hasRNAStructure = false;
 
-  /** DOCUMENT ME!! */
   public AlignmentAnnotation[] annotations;
 
-  HiddenSequences hiddenSequences = new HiddenSequences(this);
+  HiddenSequences hiddenSequences;
 
   public Hashtable alignmentProperties;
 
-  private Set<AlignedCodonFrame> codonFrameList = new LinkedHashSet<AlignedCodonFrame>();
+  private List<AlignedCodonFrame> codonFrameList;
 
   private void initAlignment(SequenceI[] seqs)
   {
-    int i = 0;
+    groups = Collections.synchronizedList(new ArrayList<SequenceGroup>());
+    hiddenSequences = new HiddenSequences(this);
+    codonFrameList = new ArrayList<AlignedCodonFrame>();
 
-    if (jalview.util.Comparison.isNucleotide(seqs))
+    if (Comparison.isNucleotide(seqs))
     {
       type = NUCLEOTIDE;
     }
@@ -82,10 +83,9 @@ public class Alignment implements AlignmentI
       type = PROTEIN;
     }
 
-    sequences = java.util.Collections
-            .synchronizedList(new ArrayList<SequenceI>());
+    sequences = Collections.synchronizedList(new ArrayList<SequenceI>());
 
-    for (i = 0; i < seqs.length; i++)
+    for (int i = 0; i < seqs.length; i++)
     {
       sequences.add(seqs[i]);
     }
@@ -104,13 +104,12 @@ public class Alignment implements AlignmentI
       seqs[i] = new Sequence(seqs[i]);
     }
 
+    initAlignment(seqs);
+
     /*
-     * Share the same dataset sequence mappings (if any). TODO: find a better
-     * place for these to live (alignment dataset?).
+     * Share the same dataset sequence mappings (if any). 
      */
-    this.codonFrameList = ((Alignment) al).codonFrameList;
-
-    initAlignment(seqs);
+    this.setCodonFrames(al.getCodonFrames());
   }
 
   /**
@@ -991,25 +990,7 @@ public class Alignment implements AlignmentI
   {
     if (dataset == null && data == null)
     {
-      // Create a new dataset for this alignment.
-      // Can only be done once, if dataset is not null
-      // This will not be performed
-      SequenceI[] seqs = new SequenceI[getHeight()];
-      SequenceI currentSeq;
-      for (int i = 0; i < getHeight(); i++)
-      {
-        currentSeq = getSequenceAt(i);
-        if (currentSeq.getDatasetSequence() != null)
-        {
-          seqs[i] = currentSeq.getDatasetSequence();
-        }
-        else
-        {
-          seqs[i] = currentSeq.createDatasetSequence();
-        }
-      }
-
-      dataset = new Alignment(seqs);
+      createDatasetAlignment();
     }
     else if (dataset == null && data != null)
     {
@@ -1040,6 +1021,37 @@ public class Alignment implements AlignmentI
   }
 
   /**
+   * Creates a new dataset for this alignment. Can only be done once - if
+   * dataset is not null this will not be performed.
+   */
+  public void createDatasetAlignment()
+  {
+    if (dataset != null)
+    {
+      return;
+    }
+    SequenceI[] seqs = new SequenceI[getHeight()];
+    SequenceI currentSeq;
+    for (int i = 0; i < getHeight(); i++)
+    {
+      currentSeq = getSequenceAt(i);
+      if (currentSeq.getDatasetSequence() != null)
+      {
+        seqs[i] = currentSeq.getDatasetSequence();
+      }
+      else
+      {
+        seqs[i] = currentSeq.createDatasetSequence();
+      }
+    }
+
+    dataset = new Alignment(seqs);
+    // move mappings to the dataset alignment
+    dataset.codonFrameList = this.codonFrameList;
+    this.codonFrameList = null;
+  }
+
+  /**
    * reference count for number of alignments referencing this one.
    */
   int alignmentRefs = 0;
@@ -1261,19 +1273,17 @@ public class Alignment implements AlignmentI
     return alignmentProperties;
   }
 
-  /*
-   * (non-Javadoc)
-   * 
-   * @see
-   * jalview.datamodel.AlignmentI#addCodonFrame(jalview.datamodel.AlignedCodonFrame
-   * )
+  /**
+   * Adds the given mapping to the stored set. Note this may be held on the
+   * dataset alignment.
    */
   @Override
   public void addCodonFrame(AlignedCodonFrame codons)
   {
-    if (codons != null)
+    List<AlignedCodonFrame> acfs = getCodonFrames();
+    if (codons != null && acfs != null && !acfs.contains(codons))
     {
-      codonFrameList.add(codons);
+      acfs.add(codons);
     }
   }
 
@@ -1291,7 +1301,7 @@ public class Alignment implements AlignmentI
       return null;
     }
     List<AlignedCodonFrame> cframes = new ArrayList<AlignedCodonFrame>();
-    for (AlignedCodonFrame acf : codonFrameList)
+    for (AlignedCodonFrame acf : getCodonFrames())
     {
       if (acf.involvesSequence(seq))
       {
@@ -1302,42 +1312,50 @@ public class Alignment implements AlignmentI
   }
 
   /**
-   * Sets the codon frame mappings (replacing any existing mappings).
+   * Sets the codon frame mappings (replacing any existing mappings). Note the
+   * mappings are set on the dataset alignment instead if there is one.
    * 
    * @see jalview.datamodel.AlignmentI#setCodonFrames()
    */
   @Override
-  public void setCodonFrames(Set<AlignedCodonFrame> acfs)
+  public void setCodonFrames(List<AlignedCodonFrame> acfs)
   {
-    this.codonFrameList = acfs;
+    if (dataset != null)
+    {
+      dataset.setCodonFrames(acfs);
+    }
+    else
+    {
+      this.codonFrameList = acfs;
+    }
   }
 
   /**
    * Returns the set of codon frame mappings. Any changes to the returned set
-   * will affect the alignment.
+   * will affect the alignment. The mappings are held on (and read from) the
+   * dataset alignment if there is one.
    * 
    * @see jalview.datamodel.AlignmentI#getCodonFrames()
    */
   @Override
-  public Set<AlignedCodonFrame> getCodonFrames()
+  public List<AlignedCodonFrame> getCodonFrames()
   {
-    return codonFrameList;
+    return dataset != null ? dataset.getCodonFrames() : codonFrameList;
   }
 
-  /*
-   * (non-Javadoc)
-   * 
-   * @seejalview.datamodel.AlignmentI#removeCodonFrame(jalview.datamodel.
-   * AlignedCodonFrame)
+  /**
+   * Removes the given mapping from the stored set. Note that the mappings are
+   * held on the dataset alignment if there is one.
    */
   @Override
   public boolean removeCodonFrame(AlignedCodonFrame codons)
   {
-    if (codons == null || codonFrameList == null)
+    List<AlignedCodonFrame> acfs = getCodonFrames();
+    if (codons == null || acfs == null)
     {
       return false;
     }
-    return codonFrameList.remove(codons);
+    return acfs.remove(codons);
   }
 
   @Override
@@ -1383,7 +1401,7 @@ public class Alignment implements AlignmentI
       addAnnotation(alan[a]);
     }
 
-    this.codonFrameList.addAll(toappend.getCodonFrames());
+    getCodonFrames().addAll(toappend.getCodonFrames());
 
     List<SequenceGroup> sg = toappend.getGroups();
     if (sg != null)
@@ -1595,6 +1613,7 @@ public class Alignment implements AlignmentI
    * 
    * @return the representative sequence for this group
    */
+  @Override
   public SequenceI getSeqrep()
   {
     return seqrep;
@@ -1607,6 +1626,7 @@ public class Alignment implements AlignmentI
    * @param seqrep
    *          the seqrep to set (null means no sequence representative)
    */
+  @Override
   public void setSeqrep(SequenceI seqrep)
   {
     this.seqrep = seqrep;
@@ -1616,6 +1636,7 @@ public class Alignment implements AlignmentI
    * 
    * @return true if group has a sequence representative
    */
+  @Override
   public boolean hasSeqrep()
   {
     return seqrep != null;
@@ -1683,31 +1704,13 @@ public class Alignment implements AlignmentI
           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)
     {
       return AlignmentUtils.alignProteinAsDna(this, al);
     }
-
-    char thisGapChar = this.getGapCharacter();
-    String gap = thisIsNucleotide && thatIsProtein ? String
-            .valueOf(new char[] { thisGapChar, thisGapChar, thisGapChar })
-            : String.valueOf(thisGapChar);
-
-    // TODO handle intron regions? Needs a 'holistic' alignment of dna,
-    // not just sequence by sequence. But how to 'gap' intron regions?
-
-    /*
-     * 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;
+    return AlignmentUtils.alignAs(this, al);
   }
 
   /**
@@ -1748,4 +1751,82 @@ public class Alignment implements AlignmentI
     }
     return hasValidSeq;
   }
+
+  /**
+   * Update any mappings to 'virtual' sequences to compatible real ones, if
+   * present in the added sequences. Returns a count of mappings updated.
+   * 
+   * @param seqs
+   * @return
+   */
+  @Override
+  public int realiseMappings(List<SequenceI> seqs)
+  {
+    int count = 0;
+    for (SequenceI seq : seqs)
+    {
+      for (AlignedCodonFrame mapping : getCodonFrames())
+      {
+        count += mapping.realiseWith(seq);
+      }
+    }
+    return count;
+  }
+
+  /**
+   * Returns the first AlignedCodonFrame that has a mapping between the given
+   * dataset sequences
+   * 
+   * @param mapFrom
+   * @param mapTo
+   * @return
+   */
+  @Override
+  public AlignedCodonFrame getMapping(SequenceI mapFrom, SequenceI mapTo)
+  {
+    for (AlignedCodonFrame acf : getCodonFrames())
+    {
+      if (acf.getAaForDnaSeq(mapFrom) == mapTo)
+      {
+        return acf;
+      }
+    }
+    return null;
+  }
+
+  @Override
+  public int[] getVisibleStartAndEndIndex(List<int[]> hiddenCols)
+  {
+    int[] alignmentStartEnd = new int[] { 0, getWidth() - 1 };
+    int startPos = alignmentStartEnd[0];
+    int endPos = alignmentStartEnd[1];
+
+    int[] lowestRange = new int[] { -1, -1 };
+    int[] higestRange = new int[] { -1, -1 };
+
+    for (int[] hiddenCol : hiddenCols)
+    {
+      lowestRange = (hiddenCol[0] <= startPos) ? hiddenCol : lowestRange;
+      higestRange = (hiddenCol[1] >= endPos) ? hiddenCol : higestRange;
+    }
+
+    if (lowestRange[0] == -1 && lowestRange[1] == -1)
+    {
+      startPos = alignmentStartEnd[0];
+    }
+    else
+    {
+      startPos = lowestRange[1] + 1;
+    }
+
+    if (higestRange[0] == -1 && higestRange[1] == -1)
+    {
+      endPos = alignmentStartEnd[1];
+    }
+    else
+    {
+      endPos = higestRange[0] - 1;
+    }
+    return new int[] { startPos, endPos };
+  }
 }