JAL=2674 Removed getHiddenColumnsCopy
[jalview.git] / src / jalview / datamodel / HiddenColumns.java
index 9f3b929..c77fb7b 100644 (file)
@@ -27,6 +27,7 @@ import java.util.BitSet;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.NoSuchElementException;
 import java.util.Vector;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
@@ -73,6 +74,49 @@ public class HiddenColumns
   }
 
   /**
+   * Copy constructor within bounds and with offset. Copies hidden column
+   * regions fully contained between start and end, and offsets positions by
+   * subtracting offset.
+   * 
+   * @param copy
+   *          HiddenColumns instance to copy from
+   * @param start
+   *          lower bound to copy from
+   * @param end
+   *          upper bound to copy to
+   * @param offset
+   *          offset to subtract from each region boundary position
+   * 
+   */
+  public HiddenColumns(HiddenColumns copy, int start, int end, int offset)
+  {
+    try
+    {
+      LOCK.writeLock().lock();
+      if (copy != null)
+      {
+        hiddenColumns = new ArrayList<>();
+        Iterator<int[]> it = copy.getBoundedIterator(start, end);
+        while (it.hasNext())
+        {
+          int[] region = it.next();
+          // still need to check boundaries because iterator returns
+          // all overlapping regions and we need contained regions
+          if (region[0] >= start && region[1] <= end)
+          {
+            hiddenColumns.add(
+                    new int[]
+            { region[0] - offset, region[1] - offset });
+          }
+        }
+      }
+    } finally
+    {
+      LOCK.writeLock().unlock();
+    }
+  }
+
+  /**
    * This method is used to return all the HiddenColumn regions and is intended
    * to remain private. External callers which need a copy of the regions can
    * call getHiddenColumnsCopyAsList.
@@ -143,6 +187,28 @@ public class HiddenColumns
     }
   }
 
+  /**
+   * Get the number of distinct hidden regions
+   * 
+   * @return number of regions
+   */
+  public int getNumberOfRegions()
+  {
+    try
+    {
+      LOCK.readLock().lock();
+      int num = 0;
+      if (hasHiddenColumns())
+      {
+        num = hiddenColumns.size();
+      }
+      return num;
+    } finally
+    {
+      LOCK.readLock().unlock();
+    }
+  }
+
   @Override
   public boolean equals(Object obj)
   {
@@ -649,54 +715,26 @@ public class HiddenColumns
   }
 
   /**
-   * Returns a copy of the vector of hidden regions, as an ArrayList. Before
-   * using this method please consider if you really need access to the hidden
-   * regions - a new (or existing!) method on HiddenColumns might be more
-   * appropriate.
-   * 
-   * @return hidden regions as an ArrayList of [start,end] pairs
-   */
-  public ArrayList<int[]> getHiddenColumnsCopy()
-  {
-    try
-    {
-      LOCK.readLock().lock();
-      return copyHiddenRegionsToArrayList(0);
-    } finally
-    {
-      LOCK.readLock().unlock();
-    }
-  }
-
-  /**
    * return all visible segments between the given start and end boundaries
    * 
    * @param start
    *          (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)
         {
@@ -710,9 +748,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;
 
@@ -725,21 +762,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();
@@ -1197,16 +1229,17 @@ public class HiddenColumns
     // but where each hidden column region is shifted backwards by the number of
     // preceding visible gaps
     // update hidden columns at the same time
-    ArrayList<int[]> regions = getHiddenColumnsCopy();
+    Iterator<int[]> regions = iterator();
     ArrayList<int[]> newhidden = new ArrayList<>();
 
     int numGapsBefore = 0;
     int gapPosition = 0;
-    for (int[] region : regions)
+    while (regions.hasNext())
     {
       // get region coordinates accounting for gaps
       // we can rely on gaps not being *in* hidden regions because we already
       // removed those
+      int[] region = regions.next();
       while (gapPosition < region[0])
       {
         gapPosition++;
@@ -1448,16 +1481,32 @@ public class HiddenColumns
     }
   }
 
-  public Iterator<int[]> getBoundedIterator(int start, int end,
-          boolean useCopy)
+  public Iterator<int[]> iterator()
   {
-    return new BoundedHiddenColsIterator(start, end, useCopy);
+    if (hiddenColumns != null)
+    {
+      int last = hiddenColumns.get(hiddenColumns.size() - 1)[1];
+      return new BoundedHiddenColsIterator(0, last, true);
+    }
+    else
+    {
+      return new BoundedHiddenColsIterator(0, 0, true);
+    }
   }
 
-  public Iterator<Integer> getBoundedStartIterator(int start, int end,
-          boolean useCopy)
+  public Iterator<int[]> getBoundedIterator(int start, int end)
   {
-    return new BoundedStartRegionIterator(start, end, useCopy);
+    return new BoundedHiddenColsIterator(start, end, true);
+  }
+
+  public Iterator<Integer> getBoundedStartIterator(int start, int end)
+  {
+    return new BoundedStartRegionIterator(start, end, true);
+  }
+
+  public Iterator<Integer> getVisibleColsIterator(int start, int end)
+  {
+    return new VisibleColsIterator(start, end, true);
   }
 
   /**
@@ -1466,11 +1515,8 @@ public class HiddenColumns
    * @author kmourao
    *
    */
-
-
   class BoundedHiddenColsIterator implements Iterator<int[]>
   {
-
     private int start; // start position to iterate from
 
     private int end; // end position to iterate to
@@ -1495,13 +1541,6 @@ public class HiddenColumns
      *          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)
@@ -1670,4 +1709,133 @@ public class HiddenColumns
       return result;
     }
   }
+
+  public class VisibleColsIterator implements Iterator<Integer>
+  {
+    private int last;
+
+    private int current;
+
+    private int next;
+
+    private List<int[]> localHidden = new ArrayList<>();
+
+    private int lasthiddenregion;
+
+    public VisibleColsIterator(int firstcol, int lastcol, boolean useCopy)
+    {
+      last = lastcol;
+      current = firstcol;
+      next = firstcol;
+      lasthiddenregion = -1;
+
+      try
+      {
+        if (useCopy)
+        {
+          // assume that if useCopy is false the calling code has locked
+          // hiddenColumns
+          LOCK.readLock().lock();
+        }
+
+        if (hiddenColumns != null)
+        {
+          int i = 0;
+          for (i = 0; i < hiddenColumns.size(); ++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;
+            }
+            if (current < hiddenColumns.get(i)[0])
+            {
+              break;
+            }
+          }
+
+          lasthiddenregion = i - 1;
+
+          for (i = hiddenColumns.size() - 1; i >= 0; --i)
+          {
+            if (last >= hiddenColumns.get(i)[0]
+                    && last <= hiddenColumns.get(i)[1])
+            {
+              // last is hidden, move to left
+              last = hiddenColumns.get(i)[0] - 1;
+            }
+            if (last > hiddenColumns.get(i)[1])
+            {
+              break;
+            }
+          }
+
+          // make a local copy of the bit we need
+          i = lasthiddenregion + 1;
+          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++;
+          }
+          lasthiddenregion = -1;
+        }
+      } finally
+      {
+        if (useCopy)
+        {
+          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)
+              && (lasthiddenregion + 1 < localHidden.size()))
+      {
+        // still some more hidden regions
+        if (next + 1 < localHidden.get(lasthiddenregion + 1)[0])
+        {
+          // next+1 is still before the next hidden region
+          next++;
+        }
+        else if ((next + 1 >= localHidden.get(lasthiddenregion + 1)[0])
+                && (next + 1 <= localHidden.get(lasthiddenregion + 1)[1]))
+        {
+          // next + 1 is in the next hidden region
+          next = localHidden.get(lasthiddenregion + 1)[1] + 1;
+          lasthiddenregion++;
+        }
+      }
+      else
+      {
+        // finished with hidden regions, just increment normally
+        next++;
+      }
+      return current;
+    }
+
+    @Override
+    public void remove()
+    {
+      throw new UnsupportedOperationException();
+    }
+  }
 }