Merge branch 'develop' into update_212_Dec_merge_with_21125_chamges
[jalview.git] / src / jalview / datamodel / Alignment.java
index 97947b6..5232f8e 100755 (executable)
@@ -29,10 +29,12 @@ import jalview.util.MessageManager;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.BitSet;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.Hashtable;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -45,11 +47,11 @@ import java.util.Vector;
  * @author JimP
  * 
  */
-public class Alignment implements AlignmentI
+public class Alignment implements AlignmentI, AutoCloseable
 {
   private Alignment dataset;
 
-  protected List<SequenceI> sequences;
+  private List<SequenceI> sequences;
 
   protected List<SequenceGroup> groups;
 
@@ -193,11 +195,13 @@ public class Alignment implements AlignmentI
   {
     synchronized (sequences)
     {
+
       if (i > -1 && i < sequences.size())
       {
         return sequences.get(i);
       }
     }
+
     return null;
   }
 
@@ -288,6 +292,32 @@ public class Alignment implements AlignmentI
   }
 
   /**
+   * Inserts a sequence at a point in the alignment.
+   * 
+   * @param i
+   *          the index of the position the sequence is to be inserted in.
+   */
+  @Override
+  public void insertSequenceAt(int i, SequenceI snew)
+  {
+    synchronized (sequences)
+    {
+      if (sequences.size() > i)
+      {
+        sequences.add(i, snew);
+        return;
+
+      }
+      else
+      {
+        sequences.add(snew);
+        hiddenSequences.adjustHeightSequenceAdded();
+      }
+      return;
+    }
+  }
+
+  /**
    * DOCUMENT ME!
    * 
    * @return DOCUMENT ME!
@@ -299,15 +329,20 @@ public class Alignment implements AlignmentI
   }
 
   @Override
-  public void finalize() throws Throwable
+  public void close()
   {
     if (getDataset() != null)
     {
-      getDataset().removeAlignmentRef();
+      try
+      {
+        getDataset().removeAlignmentRef();
+      } catch (Throwable e)
+      {
+        e.printStackTrace();
+      }
     }
 
     nullReferences();
-    super.finalize();
   }
 
   /**
@@ -582,15 +617,17 @@ public class Alignment implements AlignmentI
   @Override
   public SequenceI findName(SequenceI startAfter, String token, boolean b)
   {
-
+    if (token == null)
+      return null;
     int i = 0;
     SequenceI sq = null;
     String sqname = null;
+    int nseq = sequences.size();
     if (startAfter != null)
     {
       // try to find the sequence in the alignment
       boolean matched = false;
-      while (i < sequences.size())
+      while (i < nseq)
       {
         if (getSequenceAt(i++) == startAfter)
         {
@@ -603,7 +640,7 @@ public class Alignment implements AlignmentI
         i = 0;
       }
     }
-    while (i < sequences.size())
+    while (i < nseq)
     {
       sq = getSequenceAt(i);
       sqname = sq.getName();
@@ -709,15 +746,22 @@ public class Alignment implements AlignmentI
 
     for (int i = 0; i < sequences.size(); i++)
     {
-      if (getSequenceAt(i).getLength() > maxLength)
-      {
-        maxLength = getSequenceAt(i).getLength();
-      }
+      maxLength = Math.max(maxLength, getSequenceAt(i).getLength());
     }
-
     return maxLength;
   }
 
+  @Override
+  public int getVisibleWidth()
+  {
+    int w = getWidth();
+    if (hiddenCols != null)
+    {
+      w -= hiddenCols.getSize();
+    }
+    return w;
+  }
+
   /**
    * DOCUMENT ME!
    * 
@@ -1178,7 +1222,8 @@ public class Alignment implements AlignmentI
     int maxLength = -1;
 
     SequenceI current;
-    for (int i = 0; i < sequences.size(); i++)
+    int nseq = sequences.size();
+    for (int i = 0; i < nseq; i++)
     {
       current = getSequenceAt(i);
       for (int j = current.getLength(); j > maxLength; j--)
@@ -1195,7 +1240,7 @@ public class Alignment implements AlignmentI
     maxLength++;
 
     int cLength;
-    for (int i = 0; i < sequences.size(); i++)
+    for (int i = 0; i < nseq; i++)
     {
       current = getSequenceAt(i);
       cLength = current.getLength();
@@ -1587,34 +1632,50 @@ public class Alignment implements AlignmentI
           String calcId, boolean autoCalc, SequenceI seqRef,
           SequenceGroup groupRef)
   {
-    if (annotations != null)
+    AlignmentAnnotation annot = annotations == null ? null
+            : AlignmentAnnotation.findFirstAnnotation(
+              Arrays.asList(getAlignmentAnnotation()), name, calcId,
+              autoCalc, seqRef, groupRef);
+
+    if (annot == null)
     {
-      for (AlignmentAnnotation annot : getAlignmentAnnotation())
+
+      annot = new AlignmentAnnotation(name, name, new Annotation[1], 0f, 0f,
+              AlignmentAnnotation.BAR_GRAPH);
+      annot.hasText = false;
+      if (calcId != null)
       {
-        if (annot.autoCalculated == autoCalc && (name.equals(annot.label))
-                && (calcId == null || annot.getCalcId().equals(calcId))
-                && annot.sequenceRef == seqRef
-                && annot.groupRef == groupRef)
-        {
-          return annot;
-        }
+        annot.setCalcId(calcId);
+      }
+      annot.autoCalculated = autoCalc;
+      if (seqRef != null)
+      {
+        annot.setSequenceRef(seqRef);
       }
+      annot.groupRef = groupRef;
+      addAnnotation(annot);
     }
-    AlignmentAnnotation annot = new AlignmentAnnotation(name, name,
-            new Annotation[1], 0f, 0f, AlignmentAnnotation.BAR_GRAPH);
-    annot.hasText = false;
-    if (calcId != null)
+    return annot;
+  }
+
+
+  @Override
+  public AlignmentAnnotation updateFromOrCopyAnnotation(
+          AlignmentAnnotation ala)
+  {
+    AlignmentAnnotation annot = AlignmentAnnotation.findFirstAnnotation(
+            Arrays.asList(getAlignmentAnnotation()), ala.label, ala.calcId,
+            ala.autoCalculated, ala.sequenceRef, ala.groupRef);
+    if (annot == null)
     {
-      annot.setCalcId(new String(calcId));
+      annot = new AlignmentAnnotation(ala);
+      addAnnotation(annot);
     }
-    annot.autoCalculated = autoCalc;
-    if (seqRef != null)
+    else
     {
-      annot.setSequenceRef(seqRef);
+      annot.updateAlignmentAnnotationFrom(ala);
     }
-    annot.groupRef = groupRef;
-    addAnnotation(annot);
-
+    validateAnnotation(annot);
     return annot;
   }
 
@@ -1894,8 +1955,138 @@ public class Alignment implements AlignmentI
   }
 
   @Override
-  public void setHiddenColumns(HiddenColumns cols)
+  public boolean setHiddenColumns(HiddenColumns cols)
   {
+    boolean changed = cols == null ? hiddenCols != null
+            : !cols.equals(hiddenCols);
     hiddenCols = cols;
+    return changed;
+  }
+
+  @Override
+  public void setupJPredAlignment()
+  {
+    SequenceI repseq = getSequenceAt(0);
+    setSeqrep(repseq);
+    HiddenColumns cs = new HiddenColumns();
+    cs.hideList(repseq.getInsertions());
+    setHiddenColumns(cs);
+  }
+
+  @Override
+  public HiddenColumns propagateInsertions(SequenceI profileseq,
+          AlignmentView input)
+  {
+    int profsqpos = 0;
+
+    char gc = getGapCharacter();
+    Object[] alandhidden = input.getAlignmentAndHiddenColumns(gc);
+    HiddenColumns nview = (HiddenColumns) alandhidden[1];
+    SequenceI origseq = ((SequenceI[]) alandhidden[0])[profsqpos];
+    return propagateInsertions(profileseq, origseq, nview);
+  }
+
+  /**
+   * 
+   * @param profileseq
+   *          sequence in al which corresponds to origseq
+   * @param al
+   *          alignment which is to have gaps inserted into it
+   * @param origseq
+   *          sequence corresponding to profileseq which defines gap map for
+   *          modifying al
+   */
+  private HiddenColumns propagateInsertions(SequenceI profileseq,
+          SequenceI origseq, HiddenColumns hc)
+  {
+    // take the set of hidden columns, and the set of gaps in origseq,
+    // and remove all the hidden gaps from hiddenColumns
+
+    // first get the gaps as a Bitset
+    // then calculate hidden ^ not(gap)
+    BitSet gaps = origseq.gapBitset();
+    hc.andNot(gaps);
+
+    // for each sequence in the alignment, except the profile sequence,
+    // insert gaps corresponding to each hidden region but where each hidden
+    // column region is shifted backwards by the number of preceding visible
+    // gaps update hidden columns at the same time
+    HiddenColumns newhidden = new HiddenColumns();
+
+    int numGapsBefore = 0;
+    int gapPosition = 0;
+    Iterator<int[]> it = hc.iterator();
+    while (it.hasNext())
+    {
+      int[] region = it.next();
+
+      // get region coordinates accounting for gaps
+      // we can rely on gaps not being *in* hidden regions because we already
+      // removed those
+      while (gapPosition < region[0])
+      {
+        gapPosition++;
+        if (gaps.get(gapPosition))
+        {
+          numGapsBefore++;
+        }
+      }
+
+      int left = region[0] - numGapsBefore;
+      int right = region[1] - numGapsBefore;
+
+      newhidden.hideColumns(left, right);
+      padGaps(left, right, profileseq);
+    }
+    return newhidden;
+  }
+
+  /**
+   * Pad gaps in all sequences in alignment except profileseq
+   * 
+   * @param left
+   *          position of first gap to insert
+   * @param right
+   *          position of last gap to insert
+   * @param profileseq
+   *          sequence not to pad
+   */
+  private void padGaps(int left, int right, SequenceI profileseq)
+  {
+    char gc = getGapCharacter();
+
+    // make a string with number of gaps = length of hidden region
+    StringBuilder sb = new StringBuilder();
+    for (int g = 0; g < right - left + 1; g++)
+    {
+      sb.append(gc);
+    }
+
+    // loop over the sequences and pad with gaps where required
+    for (int s = 0, ns = getHeight(); s < ns; s++)
+    {
+      SequenceI sqobj = getSequenceAt(s);
+      if ((sqobj != profileseq) && (sqobj.getLength() >= left))
+      {
+        String sq = sqobj.getSequenceAsString();
+        sqobj.setSequence(
+                sq.substring(0, left) + sb.toString() + sq.substring(left));
+      }
+    }
+  }
+
+  @Override
+  public List<SequenceI> getHmmSequences()
+  {
+    List<SequenceI> result = new ArrayList<>();
+    for (int i = 0; i < sequences.size(); i++)
+    {
+      SequenceI seq = sequences.get(i);
+      if (seq.hasHMMProfile())
+      {
+        result.add(seq);
+      }
+    }
+    return result;
   }
 }