JAL-2759 Rearranged iterators
authorkiramt <k.mourao@dundee.ac.uk>
Tue, 10 Oct 2017 08:12:45 +0000 (09:12 +0100)
committerkiramt <k.mourao@dundee.ac.uk>
Tue, 10 Oct 2017 08:12:45 +0000 (09:12 +0100)
src/jalview/datamodel/BoundedHiddenColsIterator.java [new file with mode: 0644]
src/jalview/datamodel/BoundedStartRegionIterator.java [new file with mode: 0644]
src/jalview/datamodel/HiddenColumns.java
src/jalview/datamodel/HiddenColumnsCursor.java [new file with mode: 0644]
src/jalview/datamodel/RegionsIterator.java [new file with mode: 0644]
src/jalview/datamodel/ReverseRegionsIterator.java [new file with mode: 0644]
src/jalview/datamodel/VisibleColsIterator.java [new file with mode: 0644]
src/jalview/datamodel/VisibleContigsIterator.java [new file with mode: 0644]

diff --git a/src/jalview/datamodel/BoundedHiddenColsIterator.java b/src/jalview/datamodel/BoundedHiddenColsIterator.java
new file mode 100644 (file)
index 0000000..aa3f4ad
--- /dev/null
@@ -0,0 +1,111 @@
+package jalview.datamodel;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * An iterator which iterates over hidden column regions in a range. Works with
+ * a copy of the hidden columns collection. Intended to be used by callers
+ * OUTSIDE of HiddenColumns.
+ */
+public class BoundedHiddenColsIterator implements Iterator<int[]>
+{
+  // start position to iterate from
+  private int start;
+
+  // end position to iterate to
+  private int end;
+
+  // current index in hiddenColumns
+  private int currentPosition = 0;
+
+  // current column in hiddenColumns
+  private int[] currentRegion;
+
+  // local copy or reference to hiddenColumns
+  private List<int[]> localHidden;
+
+  /**
+   * Unbounded constructor
+   */
+  BoundedHiddenColsIterator(List<int[]> hiddenColumns)
+  {
+    if (hiddenColumns != null)
+    {
+      int last = hiddenColumns.get(hiddenColumns.size() - 1)[1];
+      init(0, last, hiddenColumns);
+    }
+    else
+    {
+      init(0, 0, hiddenColumns);
+    }
+  }
+
+  /**
+   * Construct an iterator over hiddenColums bounded at [lowerBound,upperBound]
+   * 
+   * @param lowerBound
+   *          lower bound to iterate from
+   * @param upperBound
+   *          upper bound to iterate to
+   */
+  BoundedHiddenColsIterator(int lowerBound, int upperBound,
+          List<int[]> hiddenColumns)
+  {
+    init(lowerBound, upperBound, hiddenColumns);
+  }
+
+  /**
+   * Construct an iterator over hiddenColums bounded at [lowerBound,upperBound]
+   * 
+   * @param lowerBound
+   *          lower bound to iterate from
+   * @param upperBound
+   *          upper bound to iterate to
+   */
+  private void init(int lowerBound, int upperBound,
+          List<int[]> hiddenColumns)
+  {
+    start = lowerBound;
+    end = upperBound;
+
+    if (hiddenColumns != null)
+    {
+      localHidden = new ArrayList<>();
+
+      // iterate until a region overlaps with [start,end]
+      int i = 0;
+      while ((i < hiddenColumns.size())
+              && (hiddenColumns.get(i)[1] < start))
+      {
+        i++;
+      }
+
+      // iterate from start to end, adding each hidden region. Positions are
+      // absolute, and all regions which *overlap* [start,end] are added.
+      while (i < hiddenColumns.size() && (hiddenColumns.get(i)[0] <= end))
+      {
+        int[] rh = hiddenColumns.get(i);
+        int[] cp = new int[2];
+        System.arraycopy(rh, 0, cp, 0, rh.length);
+        localHidden.add(cp);
+        i++;
+      }
+    }
+  }
+
+  @Override
+  public boolean hasNext()
+  {
+    return (localHidden != null) && (currentPosition < localHidden.size());
+  }
+
+  @Override
+  public int[] next()
+  {
+    currentRegion = localHidden.get(currentPosition);
+    currentPosition++;
+    return currentRegion;
+  }
+}
diff --git a/src/jalview/datamodel/BoundedStartRegionIterator.java b/src/jalview/datamodel/BoundedStartRegionIterator.java
new file mode 100644 (file)
index 0000000..831e906
--- /dev/null
@@ -0,0 +1,94 @@
+package jalview.datamodel;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * An iterator which iterates over visible start positions of hidden column
+ * regions in a range.
+ */
+public class BoundedStartRegionIterator implements Iterator<Integer>
+{
+  // start position to iterate from
+  private int start;
+
+  // end position to iterate to
+  private int end;
+
+  // current index in hiddenColumns
+  private int currentPosition = 0;
+
+  // local copy or reference to hiddenColumns
+  private List<Integer> positions = null;
+
+  /**
+   * Construct an iterator over hiddenColums bounded at [lowerBound,upperBound]
+   * 
+   * @param lowerBound
+   *          lower bound to iterate from
+   * @param upperBound
+   *          upper bound to iterate to
+   * @param useCopyCols
+   *          whether to make a local copy of hiddenColumns for iteration (set
+   *          to true if calling from outwith the HiddenColumns class)
+   */
+  BoundedStartRegionIterator(int lowerBound, int upperBound,
+          List<int[]> hiddenColumns)
+  {
+    start = lowerBound;
+    end = upperBound;
+
+    if (hiddenColumns != null)
+    {
+      positions = new ArrayList<>(hiddenColumns.size());
+
+      // navigate to start, keeping count of hidden columns
+      int i = 0;
+      int hiddenSoFar = 0;
+      while ((i < hiddenColumns.size())
+              && (hiddenColumns.get(i)[0] < start + hiddenSoFar))
+      {
+        int[] region = hiddenColumns.get(i);
+        hiddenSoFar += region[1] - region[0] + 1;
+        i++;
+      }
+
+      // iterate from start to end, adding start positions of each
+      // hidden region. Positions are visible columns count, not absolute
+      while (i < hiddenColumns.size()
+              && (hiddenColumns.get(i)[0] <= end + hiddenSoFar))
+      {
+        int[] region = hiddenColumns.get(i);
+        positions.add(region[0] - hiddenSoFar);
+        hiddenSoFar += region[1] - region[0] + 1;
+        i++;
+      }
+    }
+    else
+    {
+      positions = new ArrayList<>();
+    }
+
+  }
+
+  @Override
+  public boolean hasNext()
+  {
+    return (currentPosition < positions.size());
+  }
+
+  /**
+   * Get next hidden region start position
+   * 
+   * @return the start position in *visible* coordinates
+   */
+  @Override
+  public Integer next()
+  {
+    int result = positions.get(currentPosition);
+    currentPosition++;
+    return result;
+  }
+}
+
index 399e083..1ac6015 100644 (file)
@@ -24,7 +24,6 @@ import java.util.ArrayList;
 import java.util.BitSet;
 import java.util.Iterator;
 import java.util.List;
-import java.util.NoSuchElementException;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 public class HiddenColumns
@@ -33,6 +32,8 @@ public class HiddenColumns
 
   private static final ReentrantReadWriteLock LOCK = new ReentrantReadWriteLock();
 
+  private HiddenColumnsCursor cursor = new HiddenColumnsCursor();
+
   /*
    * list of hidden column [start, end] ranges; the list is maintained in
    * ascending start column order
@@ -66,6 +67,7 @@ public class HiddenColumns
           {
             hiddenColumns.add(it.next());
           }
+          cursor.resetCursor(hiddenColumns);
         }
       }
     } finally
@@ -110,6 +112,7 @@ public class HiddenColumns
             { region[0] - offset, region[1] - offset });
           }
         }
+        cursor.resetCursor(hiddenColumns);
       }
     } finally
     {
@@ -263,6 +266,9 @@ public class HiddenColumns
 
       if (hiddenColumns != null)
       {
+        result += cursor.getHiddenOffset(column);
+
+        /*   
         Iterator<int[]> it = hiddenColumns.iterator();
         while (it.hasNext())
         {
@@ -271,7 +277,7 @@ public class HiddenColumns
           {
             result += region[1] - region[0] + 1;
           }
-        }
+        }*/
       }
 
       return result;
@@ -300,7 +306,7 @@ public class HiddenColumns
       if (hiddenColumns != null)
       {
         Iterator<int[]> it = new RegionsIterator(0,
-                hiddenColumn);
+                hiddenColumn, hiddenColumns, cursor);
         while (it.hasNext())
         {
           region = it.next();
@@ -362,7 +368,8 @@ public class HiddenColumns
       // in case startColumn is in a hidden region, move it to the left
       int start = adjustForHiddenColumns(findColumnPosition(startColumn));
 
-      Iterator<int[]> it = new ReverseRegionsIterator(0, start);
+      Iterator<int[]> it = new ReverseRegionsIterator(0, start,
+              hiddenColumns);
 
       while (it.hasNext() && (distance > 0))
       {
@@ -437,7 +444,8 @@ public class HiddenColumns
     {
       LOCK.readLock().lock();
 
-      Iterator<int[]> it = new ReverseRegionsIterator(0, alPos);
+      Iterator<int[]> it = new ReverseRegionsIterator(0, alPos,
+              hiddenColumns);
       while (it.hasNext())
       {
         int[] region = it.next();
@@ -503,6 +511,10 @@ public class HiddenColumns
           added = insertRangeAtRegion(i, start, end);
         } // for
       }
+      if (!wasAlreadyLocked)
+      {
+        cursor.resetCursor(hiddenColumns);
+      }
     } finally
     {
       if (!wasAlreadyLocked)
@@ -590,7 +602,8 @@ public class HiddenColumns
     {
       LOCK.readLock().lock();
 
-      Iterator<int[]> it = new RegionsIterator(column, column);
+      Iterator<int[]> it = new RegionsIterator(column, column,
+              hiddenColumns, cursor);
       while (it.hasNext())
       {
         int[] region = it.next();
@@ -633,7 +646,7 @@ public class HiddenColumns
           StringBuffer visibleSeq = new StringBuffer();
 
           Iterator<int[]> blocks = new VisibleContigsIterator(start,
-                  end + 1, false);
+                  end + 1, hiddenColumns);
 
           while (blocks.hasNext())
           {
@@ -796,7 +809,7 @@ public class HiddenColumns
     int w = 0;
     
     Iterator<int[]> blocks = new VisibleContigsIterator(start, end + 1,
-            false);
+            hiddenColumns);
 
     int copylength;
     int annotationLength;
@@ -893,6 +906,7 @@ public class HiddenColumns
       {
         hideColumns(r[0], r[1]);
       }
+      cursor.resetCursor(hiddenColumns);
     } finally
     {
       LOCK.writeLock().unlock();
@@ -917,6 +931,7 @@ public class HiddenColumns
         }
       }
       hiddenColumns = null;
+      cursor.resetCursor(hiddenColumns);
     } finally
     {
       LOCK.writeLock().unlock();
@@ -934,7 +949,8 @@ public class HiddenColumns
     try
     {
       LOCK.writeLock().lock();
-      Iterator<int[]> it = new RegionsIterator(start, start);
+      Iterator<int[]> it = new RegionsIterator(start, start, hiddenColumns,
+              cursor);
       while (it.hasNext())
       {
         int[] region = it.next();
@@ -957,6 +973,7 @@ public class HiddenColumns
       {
         hiddenColumns = null;
       }
+      cursor.resetCursor(hiddenColumns);
     } finally
     {
       LOCK.writeLock().unlock();
@@ -1061,6 +1078,7 @@ public class HiddenColumns
 
       }
       hiddenColumns = newhidden;
+      cursor.resetCursor(hiddenColumns);
     } finally
     {
       LOCK.writeLock().unlock();
@@ -1164,6 +1182,7 @@ public class HiddenColumns
         lastSet = inserts.nextClearBit(firstSet);
         hideColumns(firstSet, lastSet - 1);
       }
+      cursor.resetCursor(hiddenColumns);
     } finally
     {
       LOCK.writeLock().unlock();
@@ -1250,7 +1269,6 @@ public class HiddenColumns
     {
       LOCK.readLock().unlock();
     }
-
   }
 
   /**
@@ -1269,7 +1287,7 @@ public class HiddenColumns
 
       int[] reveal = null;
       Iterator<int[]> it = new RegionsIterator(adjres - 2,
-              adjres + 2);
+              adjres + 2, hiddenColumns, cursor);
       while (it.hasNext())
       {
         int[] region = it.next();
@@ -1291,7 +1309,14 @@ public class HiddenColumns
    */
   public Iterator<int[]> iterator()
   {
-    return new BoundedHiddenColsIterator();
+    try
+    {
+      LOCK.readLock().lock();
+      return new BoundedHiddenColsIterator(hiddenColumns);
+    } finally
+    {
+      LOCK.readLock().unlock();
+    }
   }
 
   /**
@@ -1305,7 +1330,14 @@ public class HiddenColumns
    */
   public Iterator<int[]> getBoundedIterator(int start, int end)
   {
-    return new BoundedHiddenColsIterator(start, end);
+    try
+    {
+      LOCK.readLock().lock();
+      return new BoundedHiddenColsIterator(start, end, hiddenColumns);
+    } finally
+    {
+      LOCK.readLock().unlock();
+    }
   }
 
   /**
@@ -1319,7 +1351,14 @@ public class HiddenColumns
    */
   public Iterator<Integer> getBoundedStartIterator(int start, int end)
   {
-    return new BoundedStartRegionIterator(start, end, true);
+    try
+    {
+      LOCK.readLock().lock();
+      return new BoundedStartRegionIterator(start, end, hiddenColumns);
+    } finally
+    {
+      LOCK.readLock().unlock();
+    }
   }
 
   /**
@@ -1333,7 +1372,14 @@ public class HiddenColumns
    */
   public Iterator<Integer> getVisibleColsIterator(int start, int end)
   {
-    return new VisibleColsIterator(start, end);
+    try
+    {
+      LOCK.readLock().lock();
+      return new VisibleColsIterator(start, end, hiddenColumns);
+    } finally
+    {
+      LOCK.readLock().unlock();
+    }
   }
 
   /**
@@ -1347,8 +1393,14 @@ public class HiddenColumns
    */
   public Iterator<int[]> getVisContigsIterator(int start, int end)
   {
-    // return new VisibleBlocksIterator(start, end, true)
-    return new VisibleContigsIterator(start, end, true);
+    try
+    {
+      LOCK.readLock().lock();
+      return new VisibleContigsIterator(start, end, hiddenColumns);
+    } finally
+    {
+      LOCK.readLock().unlock();
+    }
   }
 
   /**
@@ -1378,596 +1430,15 @@ public class HiddenColumns
     }
     else
     {
-      return new VisibleContigsIterator(start, end + 1, true);
-    }
-  }
-
-  /**
-   * A local iterator which iterates over hidden column regions in a range.
-   * Intended for use ONLY within the HiddenColumns class, because it works
-   * directly with the hiddenColumns collection without locking (callers should
-   * lock hiddenColumns).
-   */
-  private class RegionsIterator implements Iterator<int[]>
-  {
-    // start position to iterate from
-    private int start;
-
-    // end position to iterate to
-    private int end;
-
-    // current index in hiddenColumns
-    private int currentPosition = 0;
-
-    // current column in hiddenColumns
-    private int[] nextRegion = null;
-
-    private int[] currentRegion = null;
-
-    private int removedIndex = -1;
-
-    // Constructor with bounds
-    RegionsIterator(int lowerBound, int upperBound)
-    {
-      start = lowerBound;
-      end = upperBound;
-
-      if (hiddenColumns != null)
-      {
-        // iterate until a region overlaps with [start,end]
-        currentPosition = 0;
-        while ((currentPosition < hiddenColumns.size())
-                && (hiddenColumns.get(currentPosition)[1] < start))
-        {
-          currentPosition++;
-        }
-        if (currentPosition < hiddenColumns.size())
-        {
-          nextRegion = hiddenColumns.get(currentPosition);
-        }
-      }
-    }
-
-    @Override
-    public boolean hasNext()
-    {
-      return (hiddenColumns != null) && (nextRegion != null)
-              && (nextRegion[0] <= end);
-    }
-
-    @Override
-    public int[] next()
-    {
-      currentRegion = nextRegion;
-      currentPosition++;
-      if (currentPosition < hiddenColumns.size())
-      {
-        nextRegion = hiddenColumns.get(currentPosition);
-      }
-      else
-      {
-        nextRegion = null;
-      }
-      return currentRegion;
-    }
-
-    @Override
-    public void remove()
-    {
-      if ((currentRegion != null) && (removedIndex != currentPosition))
-      {
-        currentPosition--;
-        hiddenColumns.subList(currentPosition, currentPosition + 1).clear();
-        removedIndex = currentPosition;
-      }
-      else
-      {
-        // already removed element last returned by next()
-        // or next() has not yet been called
-        throw new IllegalStateException();
-      }
-    }
-
-  }
-
-  /**
-   * A local iterator which reverse iterates over hidden column regions in a
-   * range. Intended for use ONLY within the HiddenColumns class, because it
-   * works directly with the hiddenColumns collection without locking (callers
-   * should lock hiddenColumns).
-   */
-  private class ReverseRegionsIterator implements Iterator<int[]>
-  {
-    // start position to iterate to
-    private int start;
-
-    // end position to iterate from
-    private int end;
-
-    // current index in hiddenColumns
-    private int currentPosition = 0;
-
-    // current column in hiddenColumns
-    private int[] nextRegion = null;
-
-    // Constructor with bounds
-    ReverseRegionsIterator(int lowerBound, int upperBound)
-    {
-      init(lowerBound, upperBound);
-    }
-
-    /**
-     * Construct an iterator over hiddenColums bounded at
-     * [lowerBound,upperBound]
-     * 
-     * @param lowerBound
-     *          lower bound to iterate to
-     * @param upperBound
-     *          upper bound to iterate from
-     */
-    private void init(int lowerBound, int upperBound)
-    {
-      start = lowerBound;
-      end = upperBound;
-
-      if (hiddenColumns != null)
-      {
-        // iterate until a region overlaps with [start,end]
-        currentPosition = hiddenColumns.size() - 1;
-        while (currentPosition >= 0
-                && hiddenColumns.get(currentPosition)[1] > end)
-        {
-          currentPosition--;
-        }
-        if (currentPosition >= 0)
-        {
-          nextRegion = hiddenColumns.get(currentPosition);
-        }
-      }
-    }
-
-    @Override
-    public boolean hasNext()
-    {
-      return (hiddenColumns != null) && (nextRegion != null)
-              && (nextRegion[1] >= start);
-    }
-
-    @Override
-    public int[] next()
-    {
-      int[] region = nextRegion;
-      currentPosition--;
-      if (currentPosition >= 0)
-      {
-        nextRegion = hiddenColumns.get(currentPosition);
-      }
-      else
-      {
-        nextRegion = null;
-      }
-      return region;
-    }
-
-  }
-
-  /**
-   * An iterator which iterates over hidden column regions in a range. Works
-   * with a copy of the hidden columns collection. Intended to be used by
-   * callers OUTSIDE of HiddenColumns.
-   */
-  private class BoundedHiddenColsIterator implements Iterator<int[]>
-  {
-    // start position to iterate from
-    private int start;
-
-    // end position to iterate to
-    private int end;
-
-    // current index in hiddenColumns
-    private int currentPosition = 0;
-
-    // current column in hiddenColumns
-    private int[] currentRegion;
-
-    // local copy or reference to hiddenColumns
-    private List<int[]> localHidden;
-
-    /**
-     * Unbounded constructor
-     */
-    BoundedHiddenColsIterator()
-    {
-      if (hiddenColumns != null)
-      {
-        int last = hiddenColumns.get(hiddenColumns.size() - 1)[1];
-        init(0, last);
-      }
-      else
-      {
-        init(0, 0);
-      }
-    }
-
-    /**
-     * Construct an iterator over hiddenColums bounded at
-     * [lowerBound,upperBound]
-     * 
-     * @param lowerBound
-     *          lower bound to iterate from
-     * @param upperBound
-     *          upper bound to iterate to
-     */
-    BoundedHiddenColsIterator(int lowerBound, int upperBound)
-    {
-      init(lowerBound, upperBound);
-    }
-
-    /**
-     * Construct an iterator over hiddenColums bounded at
-     * [lowerBound,upperBound]
-     * 
-     * @param lowerBound
-     *          lower bound to iterate from
-     * @param upperBound
-     *          upper bound to iterate to
-     */
-    private void init(int lowerBound, int upperBound)
-    {
-      start = lowerBound;
-      end = upperBound;
-
       try
       {
         LOCK.readLock().lock();
-        
-        if (hiddenColumns != null)
-        {
-          localHidden = new ArrayList<>();
-
-          // iterate until a region overlaps with [start,end]
-          int i = 0;
-          while ((i < hiddenColumns.size())
-                  && (hiddenColumns.get(i)[1] < start))
-          {
-            i++;
-          }
-
-          // iterate from start to end, adding each hidden region. Positions are
-          // absolute, and all regions which *overlap* [start,end] are added.
-          while (i < hiddenColumns.size()
-                  && (hiddenColumns.get(i)[0] <= end))
-          {
-            int[] rh = hiddenColumns.get(i);
-            int[] cp = new int[2];
-            System.arraycopy(rh, 0, cp, 0, rh.length);
-            localHidden.add(cp);
-            i++;
-          }
-        }
-      }
-      finally
-      {
-        LOCK.readLock().unlock();
-      }
-    }
-
-    @Override
-    public boolean hasNext()
-    {
-      return (localHidden != null)
-              && (currentPosition < localHidden.size());
-    }
-
-    @Override
-    public int[] next()
-    {
-      currentRegion = localHidden.get(currentPosition);
-      currentPosition++;
-      return currentRegion;
-    }
-  }
-
-  /**
-   * An iterator which iterates over visible start positions of hidden column
-   * regions in a range.
-   */
-  private class BoundedStartRegionIterator implements Iterator<Integer>
-  {
-    // start position to iterate from
-    private int start;
-
-    // end position to iterate to
-    private int end;
-
-    // current index in hiddenColumns
-    private int currentPosition = 0;
-
-    // local copy or reference to hiddenColumns
-    private List<Integer> positions = null;
-
-    /**
-     * Construct an iterator over hiddenColums bounded at
-     * [lowerBound,upperBound]
-     * 
-     * @param lowerBound
-     *          lower bound to iterate from
-     * @param upperBound
-     *          upper bound to iterate to
-     * @param useCopyCols
-     *          whether to make a local copy of hiddenColumns for iteration (set
-     *          to true if calling from outwith the HiddenColumns class)
-     */
-    BoundedStartRegionIterator(int lowerBound, int upperBound,
-            boolean useCopy)
-    {
-      start = lowerBound;
-      end = upperBound;
-      
-      try
-      {
-        if (useCopy)
-        {
-          // assume that if useCopy is false the calling code has locked
-          // hiddenColumns
-          LOCK.readLock().lock();
-        }
-
-        if (hiddenColumns != null)
-        {
-          positions = new ArrayList<>(hiddenColumns.size());
-
-          // navigate to start, keeping count of hidden columns
-          int i = 0;
-          int hiddenSoFar = 0;
-          while ((i < hiddenColumns.size())
-                  && (hiddenColumns.get(i)[0] < start + hiddenSoFar))
-          {
-            int[] region = hiddenColumns.get(i);
-            hiddenSoFar += region[1] - region[0] + 1;
-            i++;
-          }
-
-          // iterate from start to end, adding start positions of each
-          // hidden region. Positions are visible columns count, not absolute
-          while (i < hiddenColumns.size()
-                  && (hiddenColumns.get(i)[0] <= end + hiddenSoFar))
-          {
-            int[] region = hiddenColumns.get(i);
-            positions.add(region[0] - hiddenSoFar);
-            hiddenSoFar += region[1] - region[0] + 1;
-            i++;
-          }
-        }
-        else
-        {
-          positions = new ArrayList<>();
-        }
+        return new VisibleContigsIterator(start, end + 1, hiddenColumns);
       } finally
-      {
-        if (useCopy)
-        {
-          LOCK.readLock().unlock();
-        }
-      }
-    }
-
-    @Override
-    public boolean hasNext()
-    {
-      return (currentPosition < positions.size());
-    }
-
-    /**
-     * Get next hidden region start position
-     * 
-     * @return the start position in *visible* coordinates
-     */
-    @Override
-    public Integer next()
-    {
-      int result = positions.get(currentPosition);
-      currentPosition++;
-      return result;
-    }
-  }
-
-  /**
-   * Iterator over the visible *columns* (not regions) as determined by the set
-   * of hidden columns. Uses a local copy of hidden columns.
-   * 
-   * @author kmourao
-   *
-   */
-  private class VisibleColsIterator implements Iterator<Integer>
-  {
-    private int last;
-
-    private int current;
-
-    private int next;
-
-    private List<int[]> localHidden = new ArrayList<>();
-
-    private int nexthiddenregion;
-
-    VisibleColsIterator(int firstcol, int lastcol)
     {
-      last = lastcol;
-      current = firstcol;
-      next = firstcol;
-      nexthiddenregion = 0;
-
-      LOCK.readLock().lock();
-
-      if (hiddenColumns != null)
-      {
-        int i = 0;
-        for (i = 0; i < hiddenColumns.size()
-                && (current <= hiddenColumns.get(i)[0]); ++i)
-        {
-          if (current >= hiddenColumns.get(i)[0]
-                  && current <= hiddenColumns.get(i)[1])
-          {
-            // current is hidden, move to right
-            current = hiddenColumns.get(i)[1] + 1;
-            next = current;
-            nexthiddenregion = i + 1;
-          }
-        }
-
-        for (i = hiddenColumns.size() - 1; i >= 0
-                && (last >= hiddenColumns.get(i)[1]); --i)
-        {
-          if (last >= hiddenColumns.get(i)[0]
-                  && last <= hiddenColumns.get(i)[1])
-          {
-            // last is hidden, move to left
-            last = hiddenColumns.get(i)[0] - 1;
-          }
-        }
-
-        // make a local copy of the bit we need
-        i = nexthiddenregion;
-        while (i < hiddenColumns.size() && hiddenColumns.get(i)[0] <= last)
-        {
-          int[] region = new int[] { hiddenColumns.get(i)[0],
-              hiddenColumns.get(i)[1] };
-          localHidden.add(region);
-          i++;
-        }
-      }
-
-      LOCK.readLock().unlock();
-    }
-
-    @Override
-    public boolean hasNext()
-    {
-      return next <= last;
-    }
-
-    @Override
-    public Integer next()
-    {
-      if (next > last)
-      {
-        throw new NoSuchElementException();
-      }
-      current = next;
-      if ((localHidden != null)
-              && (nexthiddenregion < localHidden.size()))
-      {
-        // still some more hidden regions
-        if (next + 1 < localHidden.get(nexthiddenregion)[0])
-        {
-          // next+1 is still before the next hidden region
-          next++;
-        }
-        else if ((next + 1 >= localHidden.get(nexthiddenregion)[0])
-                && (next + 1 <= localHidden.get(nexthiddenregion)[1]))
-        {
-          // next + 1 is in the next hidden region
-          next = localHidden.get(nexthiddenregion)[1] + 1;
-          nexthiddenregion++;
-        }
-      }
-      else
-      {
-        // finished with hidden regions, just increment normally
-        next++;
-      }
-      return current;
-    }
-
-    @Override
-    public void remove()
-    {
-      throw new UnsupportedOperationException();
-    }
-  }
-
-  /**
-   * An iterator which iterates over visible regions in a range.
-   */
-  private class VisibleContigsIterator implements Iterator<int[]>
-  {
-    private List<int[]> vcontigs = new ArrayList<>();
-
-    private int currentPosition = 0;
-
-    VisibleContigsIterator(int start, int end, boolean usecopy)
-    {
-      try
-      {
-        if (usecopy)
-        {
-          LOCK.readLock().lock();
-        }
-
-        if (hiddenColumns != null && hiddenColumns.size() > 0)
-        {
-          int vstart = start;
-          int hideStart;
-          int hideEnd;
-
-          for (int[] region : hiddenColumns)
-          {
-            hideStart = region[0];
-            hideEnd = region[1];
-
-            // navigate to start
-            if (hideEnd < vstart)
-            {
-              continue;
-            }
-            if (hideStart > vstart)
-            {
-              int[] contig = new int[] { vstart, hideStart - 1 };
-              vcontigs.add(contig);
-            }
-            vstart = hideEnd + 1;
-
-            // exit if we're past the end
-            if (vstart >= end)
-            {
-              break;
-            }
-          }
-
-          if (vstart < end)
-          {
-            int[] contig = new int[] { vstart, end - 1 };
-            vcontigs.add(contig);
-          }
-        }
-        else
-        {
-          int[] contig = new int[] { start, end - 1 };
-          vcontigs.add(contig);
-        }
-      } finally
-      {
-        if (usecopy)
-        {
-          LOCK.readLock().unlock();
-        }
+        LOCK.readLock().unlock();
       }
     }
-
-    @Override
-    public boolean hasNext()
-    {
-      return (currentPosition < vcontigs.size());
-    }
-
-    @Override
-    public int[] next()
-    {
-      int[] result = vcontigs.get(currentPosition);
-      currentPosition++;
-      return result;
-    }
   }
 
   /**
diff --git a/src/jalview/datamodel/HiddenColumnsCursor.java b/src/jalview/datamodel/HiddenColumnsCursor.java
new file mode 100644 (file)
index 0000000..19c2969
--- /dev/null
@@ -0,0 +1,186 @@
+package jalview.datamodel;
+
+import java.util.List;
+
+public class HiddenColumnsCursor
+{
+  // absolute position of first hidden column
+  private int firstColumn;
+
+  // absolute position of last hidden column
+  private int lastColumn;
+
+  // index of last visited region
+  private int regionIndex;
+
+  // number of hidden columns before last visited region
+  private int hiddenSoFar;
+
+  // flag to indicate if current cursor settings are valid
+  private boolean isValid;
+
+  private List<int[]> hiddenColumns;
+
+  protected HiddenColumnsCursor()
+  {
+    isValid = false;
+  }
+
+  /**
+   * Set the cursor to a position
+   * 
+   * @param first
+   *          absolute position of first hidden column
+   * @param last
+   *          absolute position of last hidden column
+   * @param index
+   *          index of last visited region
+   * @param hiddenCount
+   *          number of hidden columns before last visited region
+   */
+  protected void resetCursor(// int first, int last, int index, int hiddenCount,
+          List<int[]> hiddenCols)
+  {
+    isValid = true;
+    if ((hiddenCols != null) && (!hiddenCols.isEmpty()))
+    {
+      hiddenColumns = hiddenCols;
+      firstColumn = hiddenColumns.get(0)[0];
+      lastColumn = hiddenColumns.get(hiddenColumns.size() - 1)[1];
+      regionIndex = 0;
+      hiddenSoFar = 0;
+    }
+  }
+
+  protected void updateCursor(int index, int hiddenCount)
+  {
+    regionIndex = index;
+    hiddenSoFar = hiddenCount;
+  }
+
+  /**
+   * 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.
+   * 
+   * @param column
+   *          absolute position of a column in the alignment
+   * @return region index
+   */
+  protected int findRegionForColumn(int column)
+  {
+    if (hiddenColumns == null)
+    {
+      return -1;
+    }
+
+    if ((hiddenColumns.get(regionIndex)[0] <= column)
+            && (hiddenColumns.get(regionIndex)[1] >= column))
+    {
+      // we hit the jackpot
+      return regionIndex;
+    }
+    else if (column < firstColumn)
+    {
+      return 0;
+    }
+    else if (column > lastColumn)
+    {
+      return hiddenColumns.size();
+    }
+    else if (column > hiddenColumns.get(regionIndex)[1])
+    {
+      // iterate from where we are now, if we're lucky we'll be close by
+      // (but still better than iterating from 0)
+      while ((regionIndex < hiddenColumns.size())
+              && (hiddenColumns.get(regionIndex)[0] <= column))
+      {
+        int[] region = hiddenColumns.get(regionIndex);
+        hiddenSoFar += region[1] - region[0] + 1;
+        regionIndex++;
+      }
+
+    }
+    else // (column < hiddenColumns.get(regionIndex)[0])
+    {
+      int lastHidden = 0;
+      while ((regionIndex >= 0)
+              && (hiddenColumns.get(regionIndex)[0] > column))
+      {
+        int[] region = hiddenColumns.get(regionIndex);
+        hiddenSoFar -= lastHidden;
+        lastHidden = region[1] - region[0] + 1;
+        regionIndex--;
+      }
+      hiddenSoFar -= lastHidden;
+    }
+    return regionIndex;
+  }
+
+  protected int getHiddenOffset(int column)
+  {
+    if (hiddenColumns == null)
+    {
+      return -1;
+    }
+
+    if (column < firstColumn)
+    {
+      return 0;
+    }
+    else if ((regionIndex < hiddenColumns.size())
+      && (hiddenColumns.get(regionIndex)[0] <= column
+      + hiddenSoFar))
+    {
+
+      // iterate from where we are now, if we're lucky we'll be close by
+      // (but still better than iterating from 0)
+      while ((regionIndex < hiddenColumns.size())
+              && (hiddenColumns.get(regionIndex)[0] <= column
+                      + hiddenSoFar))
+      {
+        int[] region = hiddenColumns.get(regionIndex);
+        hiddenSoFar += region[1] - region[0] + 1;
+        regionIndex++;
+      }
+    }
+    else if (regionIndex < hiddenColumns.size())
+    {
+      int lastHidden = hiddenColumns.get(regionIndex)[1]
+              - hiddenColumns.get(regionIndex)[0] + 1;
+
+      while ((regionIndex >= 0)
+              && (hiddenColumns.get(regionIndex)[0] <= column + hiddenSoFar
+                      - lastHidden))
+      {
+        int[] region = hiddenColumns.get(regionIndex);
+        hiddenSoFar -= lastHidden;
+        lastHidden = region[1] - region[0] + 1;
+        regionIndex--;
+      }
+
+    }
+  
+    int result = hiddenSoFar;
+    if ((regionIndex >= 0) && (regionIndex < hiddenColumns.size())
+            && (hiddenColumns.get(regionIndex)[0] <= column + hiddenSoFar)
+            && (hiddenColumns.get(regionIndex)[1] >= column + hiddenSoFar))
+    {
+      int[] region = hiddenColumns.get(regionIndex);
+      result += region[1] - region[0] + 1;
+    }
+
+    return result;
+  }
+
+  /* public int findVisiblePositionFromAbsolute(int column)
+  {
+  
+  }
+  
+  public int findAbsolutePositionFromVisible(int column)
+  {
+  
+  }*/
+}
diff --git a/src/jalview/datamodel/RegionsIterator.java b/src/jalview/datamodel/RegionsIterator.java
new file mode 100644 (file)
index 0000000..d1251f8
--- /dev/null
@@ -0,0 +1,98 @@
+package jalview.datamodel;
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A local iterator which iterates over hidden column regions in a range.
+ * Intended for use ONLY within the HiddenColumns class, because it works
+ * directly with the hiddenColumns collection without locking (callers should
+ * lock hiddenColumns).
+ */
+public class RegionsIterator implements Iterator<int[]>
+{
+  // start position to iterate from
+  private int start;
+
+  // end position to iterate to
+  private int end;
+
+  // current index in hiddenColumns
+  private int currentPosition = 0;
+
+  // current column in hiddenColumns
+  private int[] nextRegion = null;
+
+  private int[] currentRegion = null;
+
+  private int removedIndex = -1;
+
+  private final List<int[]> hiddenColumns;
+
+  // Constructor with bounds
+  RegionsIterator(int lowerBound, int upperBound, List<int[]> hiddenCols,
+          HiddenColumnsCursor cursor)
+  {
+    start = lowerBound;
+    end = upperBound;
+    hiddenColumns = hiddenCols;
+
+    if (hiddenColumns != null)
+    {
+      currentPosition = cursor.findRegionForColumn(start);
+
+      // iterate until a region overlaps with [start,end]
+      /*      currentPosition = 0;
+      while ((currentPosition < hiddenColumns.size())
+              && (hiddenColumns.get(currentPosition)[1] < start))
+      {
+        currentPosition++;
+      }*/
+      if (currentPosition < hiddenColumns.size())
+      {
+        nextRegion = hiddenColumns.get(currentPosition);
+      }
+    }
+  }
+
+  @Override
+  public boolean hasNext()
+  {
+    return (hiddenColumns != null) && (nextRegion != null)
+            && (nextRegion[0] <= end);
+  }
+
+  @Override
+  public int[] next()
+  {
+    currentRegion = nextRegion;
+    currentPosition++;
+    if (currentPosition < hiddenColumns.size())
+    {
+      nextRegion = hiddenColumns.get(currentPosition);
+    }
+    else
+    {
+      nextRegion = null;
+    }
+    return currentRegion;
+  }
+
+  @Override
+  public void remove()
+  {
+    if ((currentRegion != null) && (removedIndex != currentPosition))
+    {
+      currentPosition--;
+      hiddenColumns.subList(currentPosition, currentPosition + 1).clear();
+      removedIndex = currentPosition;
+    }
+    else
+    {
+      // already removed element last returned by next()
+      // or next() has not yet been called
+      throw new IllegalStateException();
+    }
+  }
+
+}
diff --git a/src/jalview/datamodel/ReverseRegionsIterator.java b/src/jalview/datamodel/ReverseRegionsIterator.java
new file mode 100644 (file)
index 0000000..b511ab5
--- /dev/null
@@ -0,0 +1,75 @@
+package jalview.datamodel;
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A local iterator which reverse iterates over hidden column regions in a
+ * range. Intended for use ONLY within the HiddenColumns class, because it works
+ * directly with the hiddenColumns collection without locking (callers should
+ * lock hiddenColumns).
+ */
+public class ReverseRegionsIterator implements Iterator<int[]>
+{
+  // start position to iterate to
+  private int start;
+
+  // end position to iterate from
+  private int end;
+
+  // current index in hiddenColumns
+  private int currentPosition = 0;
+
+  // current column in hiddenColumns
+  private int[] nextRegion = null;
+
+  private final List<int[]> hiddenColumns;
+
+  // Constructor with bounds
+  ReverseRegionsIterator(int lowerBound, int upperBound,
+          List<int[]> hiddenCols)
+  {
+    hiddenColumns = hiddenCols;
+    start = lowerBound;
+    end = upperBound;
+
+    if (hiddenColumns != null)
+    {
+      // iterate until a region overlaps with [start,end]
+      currentPosition = hiddenColumns.size() - 1;
+      while (currentPosition >= 0
+              && hiddenColumns.get(currentPosition)[1] > end)
+      {
+        currentPosition--;
+      }
+      if (currentPosition >= 0)
+      {
+        nextRegion = hiddenColumns.get(currentPosition);
+      }
+    }
+  }
+
+  @Override
+  public boolean hasNext()
+  {
+    return (hiddenColumns != null) && (nextRegion != null)
+            && (nextRegion[1] >= start);
+  }
+
+  @Override
+  public int[] next()
+  {
+    int[] region = nextRegion;
+    currentPosition--;
+    if (currentPosition >= 0)
+    {
+      nextRegion = hiddenColumns.get(currentPosition);
+    }
+    else
+    {
+      nextRegion = null;
+    }
+    return region;
+  }
+
+}
diff --git a/src/jalview/datamodel/VisibleColsIterator.java b/src/jalview/datamodel/VisibleColsIterator.java
new file mode 100644 (file)
index 0000000..2fa27ed
--- /dev/null
@@ -0,0 +1,116 @@
+package jalview.datamodel;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+/**
+ * Iterator over the visible *columns* (not regions) as determined by the set of
+ * hidden columns. Uses a local copy of hidden columns.
+ * 
+ * @author kmourao
+ *
+ */
+public class VisibleColsIterator implements Iterator<Integer>
+{
+  private int last;
+
+  private int current;
+
+  private int next;
+
+  private List<int[]> localHidden = new ArrayList<>();
+
+  private int nexthiddenregion;
+
+  VisibleColsIterator(int firstcol, int lastcol, List<int[]> hiddenColumns)
+  {
+    last = lastcol;
+    current = firstcol;
+    next = firstcol;
+    nexthiddenregion = 0;
+
+    if (hiddenColumns != null)
+    {
+      int i = 0;
+      for (i = 0; i < hiddenColumns.size()
+              && (current <= hiddenColumns.get(i)[0]); ++i)
+      {
+        if (current >= hiddenColumns.get(i)[0]
+                && current <= hiddenColumns.get(i)[1])
+        {
+          // current is hidden, move to right
+          current = hiddenColumns.get(i)[1] + 1;
+          next = current;
+          nexthiddenregion = i + 1;
+        }
+      }
+
+      for (i = hiddenColumns.size() - 1; i >= 0
+              && (last >= hiddenColumns.get(i)[1]); --i)
+      {
+        if (last >= hiddenColumns.get(i)[0]
+                && last <= hiddenColumns.get(i)[1])
+        {
+          // last is hidden, move to left
+          last = hiddenColumns.get(i)[0] - 1;
+        }
+      }
+
+      // make a local copy of the bit we need
+      i = nexthiddenregion;
+      while (i < hiddenColumns.size() && hiddenColumns.get(i)[0] <= last)
+      {
+        int[] region = new int[] { hiddenColumns.get(i)[0],
+            hiddenColumns.get(i)[1] };
+        localHidden.add(region);
+        i++;
+      }
+    }
+  }
+
+  @Override
+  public boolean hasNext()
+  {
+    return next <= last;
+  }
+
+  @Override
+  public Integer next()
+  {
+    if (next > last)
+    {
+      throw new NoSuchElementException();
+    }
+    current = next;
+    if ((localHidden != null) && (nexthiddenregion < localHidden.size()))
+    {
+      // still some more hidden regions
+      if (next + 1 < localHidden.get(nexthiddenregion)[0])
+      {
+        // next+1 is still before the next hidden region
+        next++;
+      }
+      else if ((next + 1 >= localHidden.get(nexthiddenregion)[0])
+              && (next + 1 <= localHidden.get(nexthiddenregion)[1]))
+      {
+        // next + 1 is in the next hidden region
+        next = localHidden.get(nexthiddenregion)[1] + 1;
+        nexthiddenregion++;
+      }
+    }
+    else
+    {
+      // finished with hidden regions, just increment normally
+      next++;
+    }
+    return current;
+  }
+
+  @Override
+  public void remove()
+  {
+    throw new UnsupportedOperationException();
+  }
+}
diff --git a/src/jalview/datamodel/VisibleContigsIterator.java b/src/jalview/datamodel/VisibleContigsIterator.java
new file mode 100644 (file)
index 0000000..d7594f9
--- /dev/null
@@ -0,0 +1,76 @@
+package jalview.datamodel;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * An iterator which iterates over visible regions in a range.
+ */
+public class VisibleContigsIterator implements Iterator<int[]>
+{
+  private List<int[]> vcontigs = new ArrayList<>();
+
+  private int currentPosition = 0;
+
+  VisibleContigsIterator(int start, int end,
+          List<int[]> hiddenColumns)
+  {
+    if (hiddenColumns != null && hiddenColumns.size() > 0)
+    {
+      int vstart = start;
+      int hideStart;
+      int hideEnd;
+
+      for (int[] region : hiddenColumns)
+      {
+        hideStart = region[0];
+        hideEnd = region[1];
+
+        // navigate to start
+        if (hideEnd < vstart)
+        {
+          continue;
+        }
+        if (hideStart > vstart)
+        {
+          int[] contig = new int[] { vstart, hideStart - 1 };
+          vcontigs.add(contig);
+        }
+        vstart = hideEnd + 1;
+
+        // exit if we're past the end
+        if (vstart >= end)
+        {
+          break;
+        }
+      }
+
+      if (vstart < end)
+      {
+        int[] contig = new int[] { vstart, end - 1 };
+        vcontigs.add(contig);
+      }
+    }
+    else
+    {
+      int[] contig = new int[] { start, end - 1 };
+      vcontigs.add(contig);
+    }
+  }
+
+  @Override
+  public boolean hasNext()
+  {
+    return (currentPosition < vcontigs.size());
+  }
+
+  @Override
+  public int[] next()
+  {
+    int[] result = vcontigs.get(currentPosition);
+    currentPosition++;
+    return result;
+  }
+}
+