JAL-2647 Added iterators and associated tests and benchmarks
[jalview.git] / src / jalview / datamodel / HiddenColumns.java
index 8380e1f..9f3b929 100644 (file)
@@ -25,6 +25,7 @@ import jalview.util.Comparison;
 import java.util.ArrayList;
 import java.util.BitSet;
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Vector;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -62,7 +63,7 @@ public class HiddenColumns
       {
         if (copy.hiddenColumns != null)
         {
-          hiddenColumns = copy.copyHiddenRegionsToArrayList();
+          hiddenColumns = copy.copyHiddenRegionsToArrayList(0);
         }
       }
     } finally
@@ -374,7 +375,7 @@ public class HiddenColumns
         // 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))
+                && (hiddenColumns.get(i)[0] < end))
         {
           int[] region = hiddenColumns.get(i);
           positions.add(region[0] - hiddenSoFar);
@@ -622,7 +623,7 @@ public class HiddenColumns
     }
   }
 
-  private ArrayList<int[]> copyHiddenRegionsToArrayList()
+  private ArrayList<int[]> copyHiddenRegionsToArrayList(int startIndex)
   {
     int size = 0;
     if (hiddenColumns != null)
@@ -631,7 +632,7 @@ public class HiddenColumns
     }
     ArrayList<int[]> copy = new ArrayList<>(size);
 
-    for (int i = 0, j = size; i < j; i++)
+    for (int i = startIndex, j = size; i < j; i++)
     {
       int[] rh;
       int[] cp;
@@ -660,7 +661,7 @@ public class HiddenColumns
     try
     {
       LOCK.readLock().lock();
-      return copyHiddenRegionsToArrayList();
+      return copyHiddenRegionsToArrayList(0);
     } finally
     {
       LOCK.readLock().unlock();
@@ -1446,4 +1447,227 @@ public class HiddenColumns
       LOCK.readLock().unlock();
     }
   }
+
+  public Iterator<int[]> getBoundedIterator(int start, int end,
+          boolean useCopy)
+  {
+    return new BoundedHiddenColsIterator(start, end, useCopy);
+  }
+
+  public Iterator<Integer> getBoundedStartIterator(int start, int end,
+          boolean useCopy)
+  {
+    return new BoundedStartRegionIterator(start, end, useCopy);
+  }
+
+  /**
+   * An iterator which iterates over hidden column regions in a range.
+   * 
+   * @author kmourao
+   *
+   */
+
+
+  class BoundedHiddenColsIterator implements Iterator<int[]>
+  {
+
+    private int start; // start position to iterate from
+
+    private int end; // end position to iterate to
+
+    // current index in hiddenColumns
+    private int currentPosition = 0;
+
+    // current column in hiddenColumns
+    private int[] currentRegion;
+
+    // whether to make a local copy of hiddenColumns
+    private final boolean useCopy;
+
+    // local copy or reference to hiddenColumns
+    private List<int[]> localHidden;
+
+    /**
+     * Construct an iterator over hiddenColums bounded at
+     * [lowerBound,upperBound]
+     * 
+     * @param lowerBound
+     *          lower bound to iterate from
+     * @param upperBound
+     *          upper bound to iterate to
+     * @param opt
+     *          Option.OVERLAP: regions which overlap [lowerBound,upperBound]
+     *          are included Option.START: regions which start in
+     *          [lowerBound,upperBound] are included
+     * @param useAbsolutePos
+     *          have bounds and return values with reference to absolute indices
+     *          (if false, use indices for visible columns)
+     * @param useCopyCols
+     *          whether to make a local copy of hiddenColumns for iteration (set
+     *          to true if calling from outwith the HiddenColumns class)
+     */
+    BoundedHiddenColsIterator(int lowerBound, int upperBound,
+            boolean useCopyCols)
+    {
+      start = lowerBound;
+      end = upperBound;
+      useCopy = useCopyCols;
+
+      try
+      {
+        if (useCopy)
+        {
+          // assume that if useCopy is false the calling code has locked
+          // hiddenColumns
+          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;
+            int[] cp;
+            rh = hiddenColumns.get(i);
+            if (rh != null)
+            {
+              cp = new int[rh.length];
+              System.arraycopy(rh, 0, cp, 0, rh.length);
+              localHidden.add(cp);
+            }
+            i++;
+          }
+        }
+      }
+      finally
+      {
+        if (useCopy)
+        {
+          LOCK.readLock().unlock();
+        }
+      }
+    }
+
+    @Override
+    public boolean hasNext()
+    {
+      return (localHidden != null)
+              && (currentPosition < localHidden.size());
+    }
+
+    @Override
+    public int[] next()
+    {
+      currentRegion = localHidden.get(currentPosition);
+      currentPosition++;
+      return currentRegion;
+    }
+  }
+
+  class BoundedStartRegionIterator implements Iterator<Integer>
+  {
+
+    private int start; // start position to iterate from
+
+    private int end; // end position to iterate to
+
+    // 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<>();
+        }
+      } finally
+      {
+        if (useCopy)
+        {
+          LOCK.readLock().unlock();
+        }
+      }
+    }
+
+    @Override
+    public boolean hasNext()
+    {
+      return (currentPosition < positions.size());
+    }
+
+    @Override
+    public Integer next()
+    {
+      int result = positions.get(currentPosition);
+      currentPosition++;
+      return result;
+    }
+  }
 }