JAL-2759 Moved propagateInsertions out of HiddenColumns
authorkiramt <k.mourao@dundee.ac.uk>
Fri, 19 Jan 2018 12:36:12 +0000 (12:36 +0000)
committerkiramt <k.mourao@dundee.ac.uk>
Fri, 19 Jan 2018 12:36:12 +0000 (12:36 +0000)
src/jalview/datamodel/Alignment.java
src/jalview/datamodel/AlignmentI.java
src/jalview/datamodel/HiddenColumns.java
src/jalview/ws/jws1/JPredThread.java
test/jalview/datamodel/AlignmentTest.java
test/jalview/datamodel/HiddenColumnsTest.java

index 7cc2125..b6a5e0f 100755 (executable)
@@ -28,10 +28,12 @@ import jalview.util.LinkedIdentityHashSet;
 import jalview.util.MessageManager;
 
 import java.util.ArrayList;
+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;
@@ -1923,4 +1925,107 @@ public class Alignment implements AlignmentI
     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));
+      }
+    }
+  }
+
 }
index bdc48d0..5fb16d6 100755 (executable)
@@ -593,4 +593,19 @@ public interface AlignmentI extends AnnotatedCollectionI
    */
   public void setupJPredAlignment();
 
+  /**
+   * Add gaps into the sequences aligned to profileseq under the given
+   * AlignmentView
+   * 
+   * @param profileseq
+   *          sequence in al which sequences are aligned to
+   * @param input
+   *          alignment view where sequence corresponding to profileseq is first
+   *          entry
+   * @return new HiddenColumns for new alignment view, with insertions into
+   *         profileseq marked as hidden.
+   */
+  public HiddenColumns propagateInsertions(SequenceI profileseq,
+          AlignmentView input);
+
 }
index 77d1c65..6e594bd 100644 (file)
@@ -153,19 +153,9 @@ public class HiddenColumns
    */
   public void hideColumns(int start, int end)
   {
-    boolean wasAlreadyLocked = false;
     try
     {
-      // check if the write lock was already locked by this thread,
-      // as this method can be called internally in loops within HiddenColumns
-      if (!LOCK.isWriteLockedByCurrentThread())
-      {
-        LOCK.writeLock().lock();
-      }
-      else
-      {
-        wasAlreadyLocked = true;
-      }
+      LOCK.writeLock().lock();
 
       int previndex = 0;
       int prevHiddenCount = 0;
@@ -220,10 +210,7 @@ public class HiddenColumns
 
     } finally
     {
-      if (!wasAlreadyLocked)
-      {
-        LOCK.writeLock().unlock();
-      }
+      LOCK.writeLock().unlock();
     }
   }
 
@@ -396,158 +383,8 @@ public class HiddenColumns
     }
   }
 
-  /**
-   * Add gaps into the sequences aligned to profileseq under the given
-   * AlignmentView
-   * 
-   * @param profileseq
-   *          sequence in al which sequences are aligned to
-   * @param al
-   *          alignment to have gaps inserted into it
-   * @param input
-   *          alignment view where sequence corresponding to profileseq is first
-   *          entry
-   * @return new HiddenColumns for new alignment view, with insertions into
-   *         profileseq marked as hidden.
-   */
-  public static HiddenColumns propagateInsertions(SequenceI profileseq,
-          AlignmentI al, AlignmentView input)
-  {
-    int profsqpos = 0;
-
-    char gc = al.getGapCharacter();
-    Object[] alandhidden = input.getAlignmentAndHiddenColumns(gc);
-    HiddenColumns nview = (HiddenColumns) alandhidden[1];
-    SequenceI origseq = ((SequenceI[]) alandhidden[0])[profsqpos];
-    nview.propagateInsertions(profileseq, al, origseq);
-    return 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 void propagateInsertions(SequenceI profileseq, AlignmentI al,
-          SequenceI origseq)
-  {
-    try
-    {
-      LOCK.writeLock().lock();
-
-      char gc = al.getGapCharacter();
-
-      // take the set of hidden columns, and the set of gaps in origseq,
-      // and remove all the hidden gaps from hiddenColumns
-
-      // first get the non-gaps as a Bitset
-      // then calculate hidden ^ not(gap)
-      BitSet gaps = origseq.gapBitset();
-      this.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
-      List<int[]> newhidden = new ArrayList<>();
-
-      int numGapsBefore = 0;
-      int gapPosition = 0;
-      for (int[] region : hiddenColumns)
-      {
-        // 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.add(new int[] { left, right });
 
-        // make a string with number of gaps = length of hidden region
-        StringBuilder sb = new StringBuilder();
-        for (int s = 0; s < right - left + 1; s++)
-        {
-          sb.append(gc);
-        }
-        padGaps(sb, left, profileseq, al);
 
-      }
-      hiddenColumns = newhidden;
-      cursor.resetCursor(hiddenColumns);
-      numColumns = 0;
-    } finally
-    {
-      LOCK.writeLock().unlock();
-    }
-  }
-
-  /**
-   * Pad gaps in all sequences in alignment except profileseq
-   * 
-   * @param sb
-   *          gap string to insert
-   * @param left
-   *          position to insert at
-   * @param profileseq
-   *          sequence not to pad
-   * @param al
-   *          alignment to pad sequences in
-   */
-  private void padGaps(StringBuilder sb, int pos, SequenceI profileseq,
-          AlignmentI al)
-  {
-    // loop over the sequences and pad with gaps where required
-    for (int s = 0, ns = al.getHeight(); s < ns; s++)
-    {
-      SequenceI sqobj = al.getSequenceAt(s);
-      if (sqobj != profileseq)
-      {
-        String sq = al.getSequenceAt(s).getSequenceAsString();
-        if (sq.length() <= pos)
-        {
-          // pad sequence
-          int diff = pos - sq.length() - 1;
-          if (diff > 0)
-          {
-            // pad gaps
-            sq = sq + sb;
-            while ((diff = pos - sq.length() - 1) > 0)
-            {
-              if (diff >= sb.length())
-              {
-                sq += sb.toString();
-              }
-              else
-              {
-                char[] buf = new char[diff];
-                sb.getChars(0, diff, buf, 0);
-                sq += buf.toString();
-              }
-            }
-          }
-          sq += sb.toString();
-        }
-        else
-        {
-          al.getSequenceAt(s).setSequence(
-                  sq.substring(0, pos) + sb.toString() + sq.substring(pos));
-        }
-      }
-    }
-  }
 
   /*
    * Methods which only need read access to the hidden columns collection. 
@@ -1199,14 +1036,14 @@ public class HiddenColumns
 
   /**
    * 
-   * @param inserts
+   * @param updates
    *          BitSet where hidden columns will be marked
    */
-  private void andNot(BitSet updates)
+  protected void andNot(BitSet updates)
   {
     try
     {
-      LOCK.readLock().lock();
+      LOCK.writeLock().lock();
 
       BitSet hiddenBitSet = new BitSet();
       for (int[] range : hiddenColumns)
@@ -1218,7 +1055,7 @@ public class HiddenColumns
       hideColumns(hiddenBitSet);
     } finally
     {
-      LOCK.readLock().unlock();
+      LOCK.writeLock().unlock();
     }
   }
 
index a239625..23d9eb0 100644 (file)
@@ -235,8 +235,7 @@ class JPredThread extends JWS1Thread implements WSClientI
           {
             // Adjust input view for gaps
             // propagate insertions into profile
-            alhidden = HiddenColumns.propagateInsertions(profileseq, al,
-                    input);
+            alhidden = al.propagateInsertions(profileseq, input);
           }
         }
       }
index 4b5d096..2fed815 100644 (file)
@@ -34,6 +34,7 @@ import jalview.io.DataSourceType;
 import jalview.io.FileFormat;
 import jalview.io.FileFormatI;
 import jalview.io.FormatAdapter;
+import jalview.util.Comparison;
 import jalview.util.MapList;
 
 import java.io.IOException;
@@ -1321,4 +1322,119 @@ public class AlignmentTest
     // todo test coverage for annotations, mappings, groups,
     // hidden sequences, properties
   }
+
+  @Test(groups = "Functional")
+  public void testPropagateInsertions()
+  {
+    // create an alignment with no gaps - this will be the profile seq and other
+    // JPRED seqs
+    AlignmentGenerator gen = new AlignmentGenerator(false);
+    AlignmentI al = gen.generate(25, 10, 1234, 0, 0);
+
+    // get the profileseq
+    SequenceI profileseq = al.getSequenceAt(0);
+    SequenceI gappedseq = new Sequence(profileseq);
+    gappedseq.insertCharAt(5, al.getGapCharacter());
+    gappedseq.insertCharAt(6, al.getGapCharacter());
+    gappedseq.insertCharAt(7, al.getGapCharacter());
+    gappedseq.insertCharAt(8, al.getGapCharacter());
+
+    // force different kinds of padding
+    al.getSequenceAt(3).deleteChars(2, 23);
+    al.getSequenceAt(4).deleteChars(2, 27);
+    al.getSequenceAt(5).deleteChars(10, 27);
+
+    // create an alignment view with the gapped sequence
+    SequenceI[] seqs = new SequenceI[1];
+    seqs[0] = gappedseq;
+    AlignmentI newal = new Alignment(seqs);
+    HiddenColumns hidden = new HiddenColumns();
+    hidden.hideColumns(15, 17);
+
+    AlignmentView view = new AlignmentView(newal, hidden, null, true, false,
+            false);
+
+    // confirm that original contigs are as expected
+    Iterator<int[]> visible = hidden.getVisContigsIterator(0, 25, false);
+    int[] region = visible.next();
+    assertEquals("[0, 14]", Arrays.toString(region));
+    region = visible.next();
+    assertEquals("[18, 24]", Arrays.toString(region));
+
+    // propagate insertions
+    HiddenColumns result = al.propagateInsertions(profileseq, view);
+
+    // confirm that the contigs have changed to account for the gaps
+    visible = result.getVisContigsIterator(0, 25, false);
+    region = visible.next();
+    assertEquals("[0, 10]", Arrays.toString(region));
+    region = visible.next();
+    assertEquals("[14, 24]", Arrays.toString(region));
+
+    // confirm the alignment has been changed so that the other sequences have
+    // gaps inserted where the columns are hidden
+    assertFalse(Comparison.isGap(al.getSequenceAt(1).getSequence()[10]));
+    assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[11]));
+    assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[12]));
+    assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[13]));
+    assertFalse(Comparison.isGap(al.getSequenceAt(1).getSequence()[14]));
+
+  }
+
+  @Test(groups = "Functional")
+  public void testPropagateInsertionsOverlap()
+  {
+    // test propagateInsertions where gaps and hiddenColumns overlap
+
+    // create an alignment with no gaps - this will be the profile seq and other
+    // JPRED seqs
+    AlignmentGenerator gen = new AlignmentGenerator(false);
+    AlignmentI al = gen.generate(20, 10, 1234, 0, 0);
+
+    // get the profileseq
+    SequenceI profileseq = al.getSequenceAt(0);
+    SequenceI gappedseq = new Sequence(profileseq);
+    gappedseq.insertCharAt(5, al.getGapCharacter());
+    gappedseq.insertCharAt(6, al.getGapCharacter());
+    gappedseq.insertCharAt(7, al.getGapCharacter());
+    gappedseq.insertCharAt(8, al.getGapCharacter());
+
+    // create an alignment view with the gapped sequence
+    SequenceI[] seqs = new SequenceI[1];
+    seqs[0] = gappedseq;
+    AlignmentI newal = new Alignment(seqs);
+
+    // hide columns so that some overlap with the gaps
+    HiddenColumns hidden = new HiddenColumns();
+    hidden.hideColumns(7, 10);
+
+    AlignmentView view = new AlignmentView(newal, hidden, null, true, false,
+            false);
+
+    // confirm that original contigs are as expected
+    Iterator<int[]> visible = hidden.getVisContigsIterator(0, 20, false);
+    int[] region = visible.next();
+    assertEquals("[0, 6]", Arrays.toString(region));
+    region = visible.next();
+    assertEquals("[11, 19]", Arrays.toString(region));
+    assertFalse(visible.hasNext());
+
+    // propagate insertions
+    HiddenColumns result = al.propagateInsertions(profileseq, view);
+
+    // confirm that the contigs have changed to account for the gaps
+    visible = result.getVisContigsIterator(0, 20, false);
+    region = visible.next();
+    assertEquals("[0, 4]", Arrays.toString(region));
+    region = visible.next();
+    assertEquals("[7, 19]", Arrays.toString(region));
+    assertFalse(visible.hasNext());
+
+    // confirm the alignment has been changed so that the other sequences have
+    // gaps inserted where the columns are hidden
+    assertFalse(Comparison.isGap(al.getSequenceAt(1).getSequence()[4]));
+    assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[5]));
+    assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[6]));
+    assertFalse(Comparison.isGap(al.getSequenceAt(1).getSequence()[7]));
+  }
 }
index 151e31d..07a4810 100644 (file)
@@ -26,7 +26,6 @@ import static org.testng.AssertJUnit.assertFalse;
 import static org.testng.AssertJUnit.assertTrue;
 
 import jalview.analysis.AlignmentGenerator;
-import jalview.util.Comparison;
 
 import java.util.Arrays;
 import java.util.BitSet;
@@ -859,123 +858,6 @@ public class HiddenColumnsTest
   }
 
   @Test(groups = "Functional")
-  public void testPropagateInsertions()
-  {
-    // create an alignment with no gaps - this will be the profile seq and other
-    // JPRED seqs
-    AlignmentGenerator gen = new AlignmentGenerator(false);
-    AlignmentI al = gen.generate(25, 10, 1234, 0, 0);
-
-    // get the profileseq
-    SequenceI profileseq = al.getSequenceAt(0);
-    SequenceI gappedseq = new Sequence(profileseq);
-    gappedseq.insertCharAt(5, al.getGapCharacter());
-    gappedseq.insertCharAt(6, al.getGapCharacter());
-    gappedseq.insertCharAt(7, al.getGapCharacter());
-    gappedseq.insertCharAt(8, al.getGapCharacter());
-
-    // force different kinds of padding
-    al.getSequenceAt(3).deleteChars(2, 23);
-    al.getSequenceAt(4).deleteChars(2, 27);
-    al.getSequenceAt(5).deleteChars(10, 27);
-
-    // create an alignment view with the gapped sequence
-    SequenceI[] seqs = new SequenceI[1];
-    seqs[0] = gappedseq;
-    AlignmentI newal = new Alignment(seqs);
-    HiddenColumns hidden = new HiddenColumns();
-    hidden.hideColumns(15, 17);
-
-    AlignmentView view = new AlignmentView(newal, hidden, null, true, false,
-            false);
-
-    // confirm that original contigs are as expected
-    Iterator<int[]> visible = hidden.getVisContigsIterator(0, 25, false);
-    int[] region = visible.next();
-    assertEquals("[0, 14]", Arrays.toString(region));
-    region = visible.next();
-    assertEquals("[18, 24]", Arrays.toString(region));
-
-    // propagate insertions
-    HiddenColumns result = HiddenColumns.propagateInsertions(profileseq, al,
-            view);
-
-    // confirm that the contigs have changed to account for the gaps
-    visible = result.getVisContigsIterator(0, 25, false);
-    region = visible.next();
-    assertEquals("[0, 10]", Arrays.toString(region));
-    region = visible.next();
-    assertEquals("[14, 24]", Arrays.toString(region));
-
-    // confirm the alignment has been changed so that the other sequences have
-    // gaps inserted where the columns are hidden
-    assertFalse(Comparison.isGap(al.getSequenceAt(1).getSequence()[10]));
-    assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[11]));
-    assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[12]));
-    assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[13]));
-    assertFalse(Comparison.isGap(al.getSequenceAt(1).getSequence()[14]));
-
-  }
-
-  @Test(groups = "Functional")
-  public void testPropagateInsertionsOverlap()
-  {
-    // test propagateInsertions where gaps and hiddenColumns overlap
-
-    // create an alignment with no gaps - this will be the profile seq and other
-    // JPRED seqs
-    AlignmentGenerator gen = new AlignmentGenerator(false);
-    AlignmentI al = gen.generate(20, 10, 1234, 0, 0);
-
-    // get the profileseq
-    SequenceI profileseq = al.getSequenceAt(0);
-    SequenceI gappedseq = new Sequence(profileseq);
-    gappedseq.insertCharAt(5, al.getGapCharacter());
-    gappedseq.insertCharAt(6, al.getGapCharacter());
-    gappedseq.insertCharAt(7, al.getGapCharacter());
-    gappedseq.insertCharAt(8, al.getGapCharacter());
-
-    // create an alignment view with the gapped sequence
-    SequenceI[] seqs = new SequenceI[1];
-    seqs[0] = gappedseq;
-    AlignmentI newal = new Alignment(seqs);
-
-    // hide columns so that some overlap with the gaps
-    HiddenColumns hidden = new HiddenColumns();
-    hidden.hideColumns(7, 10);
-
-    AlignmentView view = new AlignmentView(newal, hidden, null, true, false,
-            false);
-
-    // confirm that original contigs are as expected
-    Iterator<int[]> visible = hidden.getVisContigsIterator(0, 20, false);
-    int[] region = visible.next();
-    assertEquals("[0, 6]", Arrays.toString(region));
-    region = visible.next();
-    assertEquals("[11, 19]", Arrays.toString(region));
-    assertFalse(visible.hasNext());
-
-    // propagate insertions
-    HiddenColumns result = HiddenColumns.propagateInsertions(profileseq, al,
-            view);
-
-    // confirm that the contigs have changed to account for the gaps
-    visible = result.getVisContigsIterator(0, 20, false);
-    region = visible.next();
-    assertEquals("[0, 4]", Arrays.toString(region));
-    region = visible.next();
-    assertEquals("[7, 19]", Arrays.toString(region));
-    assertFalse(visible.hasNext());
-
-    // confirm the alignment has been changed so that the other sequences have
-    // gaps inserted where the columns are hidden
-    assertFalse(Comparison.isGap(al.getSequenceAt(1).getSequence()[4]));
-    assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[5]));
-    assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[6]));
-    assertFalse(Comparison.isGap(al.getSequenceAt(1).getSequence()[7]));
-  }
-
-  @Test(groups = "Functional")
   public void testHasHiddenColumns()
   {
     HiddenColumns h = new HiddenColumns();