JAL-2647 more iterators
[jalview.git] / src / jalview / datamodel / HiddenColumns.java
index eda8850..82e25b9 100644 (file)
@@ -740,28 +740,20 @@ public class HiddenColumns
    *          (first column inclusive from 0)
    * @param end
    *          (last column - not inclusive)
-   * @return int[] {i_start, i_end, ..} where intervals lie in
+   * @return List<int[]> {[i_start, i_end], ..} where intervals lie in
    *         start<=i_start<=i_end<end
    */
-  public int[] getVisibleContigs(int start, int end)
+  public List<int[]> getVisibleContigs(int start, int end)
   {
     try
     {
       LOCK.readLock().lock();
+      List<int[]> vcontigs = new ArrayList<>();
       if (hiddenColumns != null && hiddenColumns.size() > 0)
       {
-        // max limit on number of visible contigs
-        // so we can dimension array
-        int maxcontigs = end - start + 1;
-        if (maxcontigs > (hiddenColumns.size() + 1) * 2)
-        {
-          maxcontigs = (hiddenColumns.size() + 1) * 2;
-        }
-        int[] vcontigs = new int[maxcontigs];
         int vstart = start;
         int hideStart;
         int hideEnd;
-        int i = 0;
 
         for (int[] region : hiddenColumns)
         {
@@ -775,9 +767,8 @@ public class HiddenColumns
           }
           if (hideStart > vstart)
           {
-            vcontigs[i * 2] = vstart;
-            vcontigs[i * 2 + 1] = hideStart - 1;
-            i++;
+            int[] contig = new int[] { vstart, hideStart - 1 };
+            vcontigs.add(contig);
           }
           vstart = hideEnd + 1;
 
@@ -790,21 +781,16 @@ public class HiddenColumns
 
         if (vstart < end)
         {
-          vcontigs[i * 2] = vstart;
-          vcontigs[i * 2 + 1] = end - 1;
-          i++;
+          int[] contig = new int[] { vstart, end - 1 };
+          vcontigs.add(contig);
         }
-
-        // copy final array into array of correct size
-        int[] trimmmedContigs = new int[i * 2];
-        System.arraycopy(vcontigs, 0, trimmmedContigs, 0, i * 2);
-
-        return trimmmedContigs;
       }
       else
       {
-        return new int[] { start, end - 1 };
+        int[] contig = new int[] { start, end - 1 };
+        vcontigs.add(contig);
       }
+      return vcontigs;
     } finally
     {
       LOCK.readLock().unlock();
@@ -1539,6 +1525,11 @@ public class HiddenColumns
     return new BoundedStartRegionIterator(start, end, useCopy);
   }
 
+  public Iterator<int[]> getBoundedVisRegionIterator(int start, int end)
+  {
+    return new BoundedVisRegionIterator(start, end, true);
+  }
+
   /**
    * An iterator which iterates over hidden column regions in a range.
    * 
@@ -1742,4 +1733,125 @@ public class HiddenColumns
       return result;
     }
   }
+
+  class BoundedVisRegionIterator implements Iterator<int[]>
+  {
+    private int start; // start position to iterate from
+
+    private int end; // end position to iterate to
+
+    // current region in visColumns
+    private int[] currentRegion;
+
+    // current index in visColumns
+    private int currentPosition = 0;
+
+    private List<int[]> vcontigs = null;
+
+    /**
+     * Construct an iterator over visibleColumn regions 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 for iteration (set to true if
+     *          calling from outwith the HiddenColumns class)
+     */
+    BoundedVisRegionIterator(int lowerBound, int upperBound,
+            boolean useCopy)
+    {
+      try
+      {
+        start = lowerBound;
+        end = upperBound;
+
+        if (useCopy)
+        {
+          // assume that if useCopy is false the calling code has locked
+          // hiddenColumns
+          LOCK.readLock().lock();
+        }
+
+        int visStart = start;
+
+        if (hiddenColumns != null)
+        {
+          vcontigs = new ArrayList<>(hiddenColumns.size() + 1);
+
+          // navigate to start, keeping count of hidden columns
+          int i = 0;
+          int[] region = null;
+          while ((i < hiddenColumns.size())
+                  && (hiddenColumns.get(i)[0] <= start))
+          {
+            i++;
+          }
+          // if there was a hidden region before (i>=1), and it ended after
+          // start
+          // and before end, adjust visStart to be just after that region
+          if (i > 0)
+          {
+            region = hiddenColumns.get(i - 1);
+            if ((region[1] > start) && (region[1] < end))
+            {
+              visStart = region[1] + 1;
+            }
+            else if (region[1] >= end)
+            {
+              // previous hidden region covers whole range [start,end]
+              // early exit - vcontigs is empty
+              return;
+            }
+          }
+
+          // 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))
+          {
+            region = hiddenColumns.get(i);
+            int[] prevVisibleRegion = new int[] { visStart, region[0] - 1 };
+            vcontigs.add(prevVisibleRegion);
+            visStart = region[1] + 1;
+            i++;
+          }
+          // add on a final visible region if needed
+          if (visStart <= end)
+          {
+            int[] lastRegion = new int[] { visStart, end };
+            vcontigs.add(lastRegion);
+          }
+        }
+        else
+        {
+          vcontigs = new ArrayList<>();
+          int[] lastRegion = new int[] { start, end };
+          vcontigs.add(lastRegion);
+        }
+      } finally
+      {
+        if (useCopy)
+        {
+          LOCK.readLock().unlock();
+        }
+      }
+    }
+
+    @Override
+    public boolean hasNext()
+    {
+      return (currentPosition < vcontigs.size());
+    }
+
+    @Override
+    public int[] next()
+    {
+      currentRegion = vcontigs.get(currentPosition);
+      currentPosition++;
+      return currentRegion;
+    }
+  }
 }