JAL-2759 numColumns reset
[jalview.git] / src / jalview / datamodel / HiddenColumns.java
index 6e594bd..9a8669f 100644 (file)
@@ -33,13 +33,13 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
  * use an iterator obtained from one of:
  * 
  * - getBoundedIterator: iterates over the hidden regions, within some bounds,
- * returning absolute positions
+ * returning *absolute* positions
  * 
  * - getBoundedStartIterator: iterates over the start positions of hidden
- * regions, within some bounds, returning visible positions
+ * regions, within some bounds, returning *visible* positions
  * 
  * - getVisContigsIterator: iterates over visible regions in a range, returning
- * absolute positions
+ * *absolute* positions
  * 
  * - getVisibleColsIterator: iterates over the visible *columns*
  * 
@@ -47,12 +47,28 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
  * numbering begins at 0 throughout this class.
  * 
  * @author kmourao
- *
+ */
+
+/* Implementation notes:
+ * 
+ * Methods which change the hiddenColumns collection should use a writeLock to
+ * prevent other threads accessing the hiddenColumns collection while changes
+ * are being made. They should also reset the hidden columns cursor, and either
+ * update the hidden columns count, or set it to 0 (so that it will later be
+ * updated when needed).
+ * 
+ * 
+ * Methods which only need read access to the hidden columns collection should
+ * use a readLock to prevent other threads changing the hidden columns
+ * collection while it is in use.
  */
 public class HiddenColumns
 {
   private static final int HASH_MULTIPLIER = 31;
 
+  private static final int NUMCOLUMNS_RESET = -1; // value of numColumns if it
+                                                  // needs to be recalculated
+
   private static final ReentrantReadWriteLock LOCK = new ReentrantReadWriteLock();
 
   /*
@@ -79,14 +95,6 @@ public class HiddenColumns
   {
   }
 
-  /* 
-   * Methods which change the hiddenColumns collection. These methods should 
-   * use a writeLock to prevent other threads accessing the hiddenColumns
-   * collection while changes are being made. They should also reset the hidden
-   * columns cursor, and either update the hidden columns count, or set it to 0 
-   * (so that it will later be updated when needed).
-   */
-
   /**
    * Copy constructor
    * 
@@ -135,7 +143,7 @@ public class HiddenColumns
             numColumns += region[1] - region[0] + 1;
           }
         }
-        cursor.resetCursor(hiddenColumns);
+        cursor = new HiddenColumnsCursor(hiddenColumns);
       }
     } finally
     {
@@ -203,10 +211,11 @@ public class HiddenColumns
 
       // reset the cursor to just before our insertion point: this saves
       // a lot of reprocessing in large alignments
-      cursor.resetCursor(hiddenColumns, previndex, prevHiddenCount);
+      cursor = new HiddenColumnsCursor(hiddenColumns, previndex,
+              prevHiddenCount);
 
       // reset the number of columns so they will be recounted
-      numColumns = 0;
+      resetNumColumns();
 
     } finally
     {
@@ -273,9 +282,9 @@ public class HiddenColumns
         }
         region[1] = Math.max(nextRegion[1], end);
 
-        // in theory this is faster than hiddenColumns.remove(i+1)
-        // benchmarking results a bit ambivalent
-        hiddenColumns.subList(i + 1, i + 2).clear();
+        // in theory hiddenColumns.subList(i + 1, i + 2).clear() is faster than
+        // hiddenColumns.remove(i+1) but benchmarking results a bit ambivalent
+        hiddenColumns.remove(i + 1);
       }
       added = true;
     }
@@ -296,8 +305,8 @@ public class HiddenColumns
       {
         hideColumns(r[0], r[1]);
       }
-      cursor.resetCursor(hiddenColumns);
-      numColumns = 0;
+      cursor = new HiddenColumnsCursor(hiddenColumns);
+      resetNumColumns();
     } finally
     {
       LOCK.writeLock().unlock();
@@ -321,8 +330,8 @@ public class HiddenColumns
         }
       }
       hiddenColumns.clear();
-      cursor.resetCursor(hiddenColumns);
-      numColumns = 0;
+      cursor = new HiddenColumnsCursor(hiddenColumns);
+      resetNumColumns();
 
     } finally
     {
@@ -366,8 +375,7 @@ public class HiddenColumns
 
             if (hiddenColumns.isEmpty())
             {
-              hiddenColumns.clear();
-              numColumns = 0;
+              resetNumColumns();
             }
             else
             {
@@ -383,15 +391,6 @@ public class HiddenColumns
     }
   }
 
-
-
-
-  /*
-   * Methods which only need read access to the hidden columns collection. 
-   * These methods should use a readLock to prevent other threads changing
-   * the hidden columns collection while it is in use.
-   */
-
   /**
    * Output regions data as a string. String is in the format:
    * reg0[0]<between>reg0[1]<delimiter>reg1[0]<between>reg1[1] ... regn[1]
@@ -442,7 +441,7 @@ public class HiddenColumns
     {
       LOCK.readLock().lock();
 
-      if (numColumns == 0)
+      if (numColumns == NUMCOLUMNS_RESET)
       {
         // numColumns is out of date, so recalculate
         int size = 0;
@@ -463,6 +462,17 @@ public class HiddenColumns
   }
 
   /**
+   * Reset numColumns so that it gets recalculated. Currently the code does not
+   * recalculate numColumns on hide/reveal as it requires a full sweep of the
+   * hidden columns collection / smarter updating. Placeholder here if later on
+   * a recalculation is added.
+   */
+  private void resetNumColumns()
+  {
+    numColumns = NUMCOLUMNS_RESET;
+  }
+
+  /**
    * Get the number of distinct hidden regions
    * 
    * @return number of regions
@@ -639,7 +649,7 @@ public class HiddenColumns
    * @return the index of the next hidden column, or alPos if there is no next
    *         hidden column
    */
-  public int getHiddenBoundaryRight(int alPos)
+  public int getNextHiddenBoundary(boolean left, int alPos)
   {
     try
     {
@@ -647,7 +657,13 @@ public class HiddenColumns
       if (!hiddenColumns.isEmpty())
       {
         int index = cursor.findRegionForColumn(alPos).getRegionIndex();
-        if (index < hiddenColumns.size())
+
+        if (left && index > 0)
+        {
+          int[] region = hiddenColumns.get(index - 1);
+          return region[1];
+        }
+        else if (!left && index < hiddenColumns.size())
         {
           int[] region = hiddenColumns.get(index);
           if (alPos < region[0])
@@ -672,38 +688,6 @@ public class HiddenColumns
   }
 
   /**
-   * This method returns the leftmost limit of a region of an alignment with
-   * hidden columns. In otherwords, the previous hidden column.
-   * 
-   * @param alPos
-   *          the absolute (visible) alignmentPosition to find the previous
-   *          hidden column for
-   */
-  public int getHiddenBoundaryLeft(int alPos)
-  {
-    try
-    {
-      LOCK.readLock().lock();
-
-      if (!hiddenColumns.isEmpty())
-      {
-        int index = cursor.findRegionForColumn(alPos).getRegionIndex();
-
-        if (index > 0)
-        {
-          int[] region = hiddenColumns.get(index - 1);
-          return region[1];
-        }
-      }
-      return alPos;
-    } finally
-    {
-      LOCK.readLock().unlock();
-    }
-  }
-
-
-  /**
    * Answers if a column in the alignment is visible
    * 
    * @param column
@@ -736,136 +720,6 @@ public class HiddenColumns
   }
 
   /**
-   * Get the visible sections of a set of sequences
-   * 
-   * @param start
-   *          sequence position to start from
-   * @param end
-   *          sequence position to end at
-   * @param seqs
-   *          an array of sequences
-   * @return an array of strings encoding the visible parts of each sequence
-   */
-  public String[] getVisibleSequenceStrings(int start, int end,
-          SequenceI[] seqs)
-  {
-    try
-    {
-      LOCK.readLock().lock();
-      int iSize = seqs.length;
-      String[] selections = new String[iSize];
-      if (!hiddenColumns.isEmpty())
-      {
-        for (int i = 0; i < iSize; i++)
-        {
-          StringBuilder visibleSeq = new StringBuilder();
-
-          Iterator<int[]> blocks = new VisibleContigsIterator(start,
-                  end + 1, hiddenColumns);
-
-          while (blocks.hasNext())
-          {
-            int[] block = blocks.next();
-            if (blocks.hasNext())
-            {
-              visibleSeq
-                      .append(seqs[i].getSequence(block[0], block[1] + 1));
-            }
-            else
-            {
-              visibleSeq
-                      .append(seqs[i].getSequence(block[0], block[1]));
-            }
-          }
-
-          selections[i] = visibleSeq.toString();
-        }
-      }
-      else
-      {
-        for (int i = 0; i < iSize; i++)
-        {
-          selections[i] = seqs[i].getSequenceAsString(start, end);
-        }
-      }
-
-      return selections;
-    } finally
-    {
-      LOCK.readLock().unlock();
-    }
-  }
-
-  /**
-   * Locate the first position visible for this sequence. If seq isn't visible
-   * then return the position of the left side of the hidden boundary region.
-   * 
-   * @param seq
-   *          sequence to find position for
-   * @return visible start position
-   */
-  public int locateVisibleStartOfSequence(SequenceI seq)
-  {
-    try
-    {
-      LOCK.readLock().lock();
-      int start = 0;
-
-      if (hiddenColumns.isEmpty())
-      {
-        return seq.findIndex(seq.getStart()) - 1;
-      }
-
-      // Simply walk along the sequence whilst watching for hidden column
-      // boundaries
-      Iterator<int[]> regions = hiddenColumns.iterator();
-      int hideStart = seq.getLength();
-      int hideEnd = -1;
-      int visPrev = 0;
-      int visNext = 0;
-      boolean foundStart = false;
-
-      // step through the non-gapped positions of the sequence
-      for (int i = seq.getStart(); i <= seq.getEnd() && (!foundStart); i++)
-      {
-        // get alignment position of this residue in the sequence
-        int p = seq.findIndex(i) - 1;
-
-        // update hidden region start/end
-        while (hideEnd < p && regions.hasNext())
-        {
-          int[] region = regions.next();
-          visPrev = visNext;
-          visNext += region[0] - visPrev;
-          hideStart = region[0];
-          hideEnd = region[1];
-        }
-        if (hideEnd < p)
-        {
-          hideStart = seq.getLength();
-        }
-        // update visible boundary for sequence
-        if (p < hideStart)
-        {
-          start = p;
-          foundStart = true;
-        }
-      }
-
-      if (foundStart)
-      {
-        return absoluteToVisibleColumn(start);
-      }
-      // otherwise, sequence was completely hidden
-      return visPrev;
-    } finally
-    {
-      LOCK.readLock().unlock();
-    }
-  }
-
-
-  /**
    * 
    * @return true if there are columns hidden
    */
@@ -911,6 +765,7 @@ public class HiddenColumns
     {
       LOCK.readLock().lock();
       int hashCode = 1;
+
       for (int[] hidden : hiddenColumns)
       {
         hashCode = HASH_MULTIPLIER * hashCode + hidden[0];
@@ -941,8 +796,8 @@ public class HiddenColumns
         lastSet = inserts.nextClearBit(firstSet);
         hideColumns(firstSet, lastSet - 1);
       }
-      cursor.resetCursor(hiddenColumns);
-      numColumns = 0;
+      cursor = new HiddenColumnsCursor(hiddenColumns);
+      resetNumColumns();
     } finally
     {
       LOCK.writeLock().unlock();
@@ -1025,8 +880,8 @@ public class HiddenColumns
         }
 
         hiddenColumns.subList(startindex, endindex + 1).clear();
-        cursor.resetCursor(hiddenColumns);
-        numColumns = 0;
+        cursor = new HiddenColumnsCursor(hiddenColumns);
+        resetNumColumns();
       }
     } finally
     {