JAL-3438 spotless for 2.11.2.0
[jalview.git] / src / jalview / datamodel / HiddenColumnsCursor.java
index 3284504..d6b6c10 100644 (file)
  */
 package jalview.datamodel;
 
+import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.atomic.AtomicReference;
 
 public class HiddenColumnsCursor
 {
   // absolute position of first hidden column
   private int firstColumn;
 
-  private List<int[]> hiddenColumns;
+  private List<int[]> hiddenColumns = new ArrayList<>();
 
-  // AtomicReference to hold the current region index and hidden column count
-  // Could be done with synchronisation but benchmarking shows this way is 2x
-  // faster
-  private final AtomicReference<HiddenCursorPosition> cursorPos = new AtomicReference<>(
-          new HiddenCursorPosition(0, 0, 0));
+  private HiddenCursorPosition cursorPos = new HiddenCursorPosition(0, 0);
 
   protected HiddenColumnsCursor()
   {
 
   }
 
-  /**
-   * Reset the cursor with a new hidden columns collection. Calls to resetCursor
-   * should be made from within a writeLock in the HiddenColumns class - since
-   * changes to the hiddenColumns collection require a writeLock the lock should
-   * already exist.
-   * 
-   * @param hiddenCols
-   */
-  protected void resetCursor(List<int[]> hiddenCols)
+  protected HiddenColumnsCursor(List<int[]> hiddenCols)
   {
-    hiddenColumns = hiddenCols;
-    if ((hiddenCols != null) && (!hiddenCols.isEmpty()))
-    {
-      firstColumn = hiddenColumns.get(0)[0];
-      HiddenCursorPosition oldpos = cursorPos.get();
-      HiddenCursorPosition newpos = new HiddenCursorPosition(0, 0, 0);
-      cursorPos.compareAndSet(oldpos, newpos);
-    }
+    resetCursor(hiddenCols, 0, 0);
+  }
+
+  protected HiddenColumnsCursor(List<int[]> hiddenCols, int index,
+          int hiddencount)
+  {
+    resetCursor(hiddenCols, index, hiddencount);
   }
 
   /**
-   * Delete the region the cursor is currently at. Avoids having to reset the
-   * cursor just because we deleted a region.
+   * Reset the cursor with a new hidden columns collection, where we know in
+   * advance the index and hidden columns count of a particular location.
    * 
-   * Calls to updateForDeletedRegion should be made from within a writeLock in
-   * the HiddenColumns class - since changes to the hiddenColumns collection
-   * require a writeLock the lock should already exist.
-   *
    * @param hiddenCols
+   *          new hidden columns collection
+   * @param index
+   *          cursor index to reset to
+   * @param hiddencount
+   *          hidden columns count to reset to
    */
-  protected void updateForDeletedRegion(List<int[]> hiddenCols)
+  private void resetCursor(List<int[]> hiddenCols, int index,
+          int hiddencount)
   {
-
-    if ((hiddenCols != null) && (!hiddenCols.isEmpty()))
+    hiddenColumns = hiddenCols;
+    if (!hiddenCols.isEmpty())
     {
-      // if there is a region to the right of the current region,
-      // nothing changes; otherwise
-      // we deleted the last region (index=hiddenCols.size()-1)
-      // or the index was at the end of the alignment (index=hiddenCols.size())
-      HiddenCursorPosition oldpos = cursorPos.get();
-
-      int index = oldpos.getRegionIndex();
-      if (index >= hiddenColumns.size() - 1)
-      {
-        // deleted last region, index is now end of alignment
-        index = hiddenCols.size();
-      }
-
-      // update the cursor position
-      HiddenCursorPosition newpos = new HiddenCursorPosition(index,
-              oldpos.getHiddenSoFar(), oldpos.getNumColumns());
-      cursorPos.compareAndSet(oldpos, newpos);
+      firstColumn = hiddenColumns.get(0)[0];
+      cursorPos = new HiddenCursorPosition(index, hiddencount);
     }
-    hiddenColumns = hiddenCols;
   }
 
   /**
-   * Get the index of the region that column is within (if column is hidden) or
-   * which is to the right of column (if column is visible). If no hidden
-   * columns are to the right, will return size of hiddenColumns. If hidden
-   * columns is empty returns -1.
+   * Get the cursor pointing to the hidden region that column is within (if
+   * column is hidden) or which is to the right of column (if column is
+   * visible). If no hidden columns are to the right, returns a cursor pointing
+   * to an imaginary hidden region beyond the end of the hidden columns
+   * collection (this ensures the count of previous hidden columns is correct).
+   * If hidden columns is empty returns null.
    * 
    * @param column
-   *          absolute position of a column in the alignment
-   * @return region index
+   *          index of column in visible or absolute coordinates
+   * @param useVisible
+   *          true if column is in visible coordinates, false if absolute
+   * @return cursor pointing to hidden region containing the column (if hidden)
+   *         or to the right of the column (if visible)
    */
-  protected HiddenCursorPosition findRegionForColumn(int column)
+  protected HiddenCursorPosition findRegionForColumn(int column,
+          boolean useVisible)
   {
-    if (hiddenColumns == null)
+    if (hiddenColumns.isEmpty())
     {
       return null;
     }
 
-    HiddenCursorPosition oldpos = cursorPos.get();
-    int index = oldpos.getRegionIndex();
-    int hiddenCount = oldpos.getHiddenSoFar();
+    // used to add in hiddenColumns offset when working with visible columns
+    int offset = (useVisible ? 1 : 0);
 
-    if (index == hiddenColumns.size())
-    {
-      // went past the end of hiddenColumns collection last time
-      index--;
-      int[] region = hiddenColumns.get(index);
-      hiddenCount -= region[1] - region[0] + 1;
-    }
+    HiddenCursorPosition pos = cursorPos;
+    int index = pos.getRegionIndex();
+    int hiddenCount = pos.getHiddenSoFar();
 
-    if ((hiddenColumns.get(index)[0] <= column)
-            && hiddenColumns.get(index)[1] >= column)
-    {
-      // column is in the current region
-      // we hit the jackpot
-      // don't need to move index
-    }
-    else if (column < firstColumn)
+    if (column < firstColumn)
     {
-      index = 0;
-      hiddenCount = 0;
+      pos = new HiddenCursorPosition(0, 0);
     }
+
     // column is after current region
-    else if (column > hiddenColumns.get(index)[1]) // includes if column >
-                                                   // lastColumn
+    else if ((index < hiddenColumns.size())
+            && (hiddenColumns.get(index)[0] <= column
+                    + offset * hiddenCount))
     {
       // iterate from where we are now, if we're lucky we'll be close by
       // (but still better than iterating from 0)
       // stop when we find the region *before* column
       // i.e. the next region starts after column or if not, ends after column
-      while ((index < hiddenColumns.size())
-              && (column > hiddenColumns.get(index)[1]))
-      {
-        int[] region = hiddenColumns.get(index);
-        hiddenCount += region[1] - region[0] + 1;
-        index++;
-      }
+      pos = searchForward(pos, column, useVisible);
     }
 
     // column is before current region
-    else if (column < hiddenColumns.get(index)[0])
+    else
     {
-      // column is before or in the previous region
-      if ((index > 0) && (hiddenColumns.get(index - 1)[1] >= column))
-      {
-        while ((index > 0) && (hiddenColumns.get(index - 1)[1] >= column))
-        {
-          index--;
-          int[] region = hiddenColumns.get(index);
-          hiddenCount -= region[1] - region[0] + 1;
-        }
-      }
+      pos = searchBackward(pos, column, useVisible);
     }
-    HiddenCursorPosition newpos = new HiddenCursorPosition(index,
-            hiddenCount, oldpos.getNumColumns());
-    cursorPos.compareAndSet(oldpos, newpos);
-    return newpos;
+    cursorPos = pos;
+    return pos;
   }
 
   /**
-   * Get the number of hidden columns in regions before column i.e. excludes
-   * hidden columns in the region column is in, if any
+   * Search forwards through the hidden columns collection to find the hidden
+   * region immediately before a column
    * 
+   * @param pos
+   *          current position
    * @param column
-   *          index of column in visible alignment
-   * @return
+   *          column to locate
+   * @param useVisible
+   *          whether using visible or absolute coordinates
+   * @return position of region before column
    */
-  protected HiddenCursorPosition getHiddenOffset(int column)
+  private HiddenCursorPosition searchForward(HiddenCursorPosition pos,
+          int column, boolean useVisible)
   {
-    if (hiddenColumns == null)
+    HiddenCursorPosition p = pos;
+    if (useVisible)
     {
-      return null;
-    }
-
-    HiddenCursorPosition oldpos = cursorPos.get();
-    int index = oldpos.getRegionIndex();
-    int hiddenCount = oldpos.getHiddenSoFar();
-
-    if (column < firstColumn)
-    {
-      index = 0;
-      hiddenCount = 0;
-    }
-    else if ((index < hiddenColumns.size())
-            && (hiddenColumns.get(index)[0] <= column + hiddenCount))
-    {
-      // iterate from where we are now, if we're lucky we'll be close by
-      // (but still better than iterating from 0)
-      while ((index < hiddenColumns.size())
-              && (hiddenColumns.get(index)[0] <= column + hiddenCount))
+      while ((p.getRegionIndex() < hiddenColumns.size())
+              && hiddenColumns.get(p.getRegionIndex())[0] <= column
+                      + p.getHiddenSoFar())
       {
-        int[] region = hiddenColumns.get(index);
-        hiddenCount += region[1] - region[0] + 1;
-        index++;
+        p = stepForward(p);
       }
     }
     else
     {
-      while ((index > 0)
-              && (hiddenColumns.get(index - 1)[1] >= column + hiddenCount))
+      while ((p.getRegionIndex() < hiddenColumns.size())
+              && hiddenColumns.get(p.getRegionIndex())[1] < column)
       {
-        index--;
-        int[] region = hiddenColumns.get(index);
-        hiddenCount -= region[1] - region[0] + 1;
+        p = stepForward(p);
       }
-
     }
+    return p;
+  }
+
+  /**
+   * Move to the next (rightwards) hidden region after a given cursor position
+   * 
+   * @param p
+   *          current position of cursor
+   * @return new position of cursor at next region
+   */
+  private HiddenCursorPosition stepForward(HiddenCursorPosition p)
+  {
+    int[] region = hiddenColumns.get(p.getRegionIndex());
 
-    HiddenCursorPosition newpos = new HiddenCursorPosition(index,
-            hiddenCount, oldpos.getNumColumns());
-    cursorPos.compareAndSet(oldpos, newpos);
-    return newpos;
+    // increment the index, and add this region's hidden columns to the hidden
+    // column count
+    return new HiddenCursorPosition(p.getRegionIndex() + 1,
+            p.getHiddenSoFar() + region[1] - region[0] + 1);
   }
+
+  /**
+   * Search backwards through the hidden columns collection to find the hidden
+   * region immediately before (left of) a given column
+   * 
+   * @param pos
+   *          current position
+   * @param column
+   *          column to locate
+   * @param useVisible
+   *          whether using visible or absolute coordinates
+   * @return position of region immediately to left of column
+   */
+  private HiddenCursorPosition searchBackward(HiddenCursorPosition p,
+          int column, boolean useVisible)
+  {
+    int i = p.getRegionIndex();
+    int h = p.getHiddenSoFar();
+
+    // used to add in hiddenColumns offset when working with visible columns
+    int offset = (useVisible ? 1 : 0);
+
+    while ((i > 0) && (hiddenColumns.get(i - 1)[1] >= column + offset * h))
+    {
+      i--;
+      int[] region = hiddenColumns.get(i);
+      h -= region[1] - region[0] + 1;
+    }
+    return new HiddenCursorPosition(i, h);
+  }
+
 }