JAL-2674 Last moves to iterators
[jalview.git] / src / jalview / datamodel / HiddenColumns.java
index 41bd16b..fb1961e 100644 (file)
@@ -25,7 +25,6 @@ import java.util.BitSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.NoSuchElementException;
-import java.util.Vector;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 public class HiddenColumns
@@ -61,7 +60,12 @@ public class HiddenColumns
       {
         if (copy.hiddenColumns != null)
         {
-          hiddenColumns = copy.copyHiddenRegionsToArrayList(0);
+          hiddenColumns = new ArrayList<>();
+          Iterator<int[]> it = copy.iterator();
+          while (it.hasNext())
+          {
+            hiddenColumns.add(it.next());
+          }
         }
       }
     } finally
@@ -129,7 +133,7 @@ public class HiddenColumns
     {
       LOCK.readLock().lock();
       StringBuilder regionBuilder = new StringBuilder();
-      Iterator<int[]> it = new LocalBoundedHiddenColsIterator();
+      Iterator<int[]> it = new RegionsIterator();
       while (it.hasNext())
       {
         int[] range = it.next();
@@ -158,7 +162,7 @@ public class HiddenColumns
     {
       LOCK.readLock().lock();
       int size = 0;
-      Iterator<int[]> it = new LocalBoundedHiddenColsIterator();
+      Iterator<int[]> it = new RegionsIterator();
       while (it.hasNext())
       {
         int[] range = it.next();
@@ -220,7 +224,7 @@ public class HiddenColumns
         return false;
       }
 
-      Iterator<int[]> it = new LocalBoundedHiddenColsIterator();
+      Iterator<int[]> it = new RegionsIterator();
       Iterator<int[]> thatit = that.iterator();
       while (it.hasNext())
       {
@@ -252,7 +256,7 @@ public class HiddenColumns
       LOCK.readLock().lock();
       int result = column;
 
-      Iterator<int[]> it = new LocalBoundedHiddenColsIterator();
+      Iterator<int[]> it = new RegionsIterator();
       while (it.hasNext())
       {
         int[] region = it.next();
@@ -287,7 +291,7 @@ public class HiddenColumns
       int[] region = null;
       if (hiddenColumns != null)
       {
-        Iterator<int[]> it = new LocalBoundedHiddenColsIterator(0,
+        Iterator<int[]> it = new RegionsIterator(0,
                 hiddenColumn);
         while (it.hasNext())
         {
@@ -306,8 +310,8 @@ public class HiddenColumns
           // earlier hidden columns.
           // Calculate the difference between the actual hidden col position
           // and region[0]-1, and then subtract from result to convert result
-          // from
-          // the adjusted hiddenColumn value to the adjusted region[0]-1 value
+          // from the adjusted hiddenColumn value to the adjusted region[0]-1
+          // value.
 
           // However, if the region begins at 0 we cannot return region[0]-1
           // just return 0
@@ -344,64 +348,48 @@ public class HiddenColumns
   {
     try
     {
-
       LOCK.readLock().lock();
       int distance = visibleDistance;
 
       // in case startColumn is in a hidden region, move it to the left
       int start = adjustForHiddenColumns(findColumnPosition(startColumn));
 
-      // get index of hidden region to left of start
-      int index = getHiddenIndexLeft(start);
-      if (index == -1)
-      {
-        // no hidden regions to left of startColumn
-        return start - distance;
-      }
-
-      // walk backwards through the alignment subtracting the counts of visible
-      // columns from distance
-      int[] region;
-      int gap = 0;
-      int nextstart = start;
+      Iterator<int[]> it = new ReverseRegionsIterator(0, start);
 
-      while ((index > -1) && (distance - gap > 0))
+      while (it.hasNext() && (distance > 0))
       {
-        // subtract the gap to right of region from distance
-        distance -= gap;
-        start = nextstart;
-
-        // calculate the next gap
-        region = hiddenColumns.get(index);
-        gap = start - region[1];
+        int[] region = it.next();
 
-        // set start to just to left of current region
-        nextstart = region[0] - 1;
-        index--;
+        if (start > region[1])
+        {
+          // subtract the gap to right of region from distance
+          if (start - region[1] <= distance)
+          {
+            distance -= start - region[1];
+            start = region[0] - 1;
+          }
+          else
+          {
+            start = start - distance;
+            distance = 0;
+          }
+        }
       }
 
-      if (distance - gap > 0)
-      {
-        // fell out of loop because there are no more hidden regions
-        distance -= gap;
-        return nextstart - distance;
-      }
       return start - distance;
+
     } finally
     {
       LOCK.readLock().unlock();
     }
-
   }
 
-
-
   /**
    * This method returns the rightmost limit of a region of an alignment with
    * hidden columns. In otherwords, the next hidden column.
    * 
-   * @param index
-   *          int
+   * @param alPos
+   *          the (visible) alignmentPosition to find the next hidden column for
    */
   public int getHiddenBoundaryRight(int alPos)
   {
@@ -410,33 +398,30 @@ public class HiddenColumns
       LOCK.readLock().lock();
       if (hiddenColumns != null)
       {
-        int index = 0;
-        do
+        Iterator<int[]> it = new RegionsIterator();
+        while (it.hasNext())
         {
-          int[] region = hiddenColumns.get(index);
+          int[] region = it.next();
           if (alPos < region[0])
           {
             return region[0];
           }
-
-          index++;
-        } while (index < hiddenColumns.size());
+        }
       }
-
       return alPos;
     } finally
     {
       LOCK.readLock().unlock();
     }
-
   }
 
   /**
    * This method returns the leftmost limit of a region of an alignment with
    * hidden columns. In otherwords, the previous hidden column.
    * 
-   * @param index
-   *          int
+   * @param alPos
+   *          the (visible) alignmentPosition to find the previous hidden column
+   *          for
    */
   public int getHiddenBoundaryLeft(int alPos)
   {
@@ -444,19 +429,14 @@ public class HiddenColumns
     {
       LOCK.readLock().lock();
 
-      if (hiddenColumns != null)
+      Iterator<int[]> it = new ReverseRegionsIterator(0, alPos);
+      while (it.hasNext())
       {
-        int index = hiddenColumns.size() - 1;
-        do
+        int[] region = it.next();
+        if (alPos > region[1])
         {
-          int[] region = hiddenColumns.get(index);
-          if (alPos > region[1])
-          {
-            return region[1];
-          }
-
-          index--;
-        } while (index > -1);
+          return region[1];
+        }
       }
 
       return alPos;
@@ -467,47 +447,12 @@ public class HiddenColumns
   }
 
   /**
-   * This method returns the index of the hidden region to the left of a column
-   * position. If the column is in a hidden region it returns the index of the
-   * region to the left. If there is no hidden region to the left it returns -1.
-   * 
-   * @param pos
-   *          int
-   */
-  private int getHiddenIndexLeft(int pos)
-  {
-    try
-    {
-
-      LOCK.readLock().lock();
-      if (hiddenColumns != null)
-      {
-        int index = hiddenColumns.size() - 1;
-        do
-        {
-          int[] region = hiddenColumns.get(index);
-          if (pos > region[1])
-          {
-            return index;
-          }
-
-          index--;
-        } while (index > -1);
-      }
-
-      return -1;
-    } finally
-    {
-      LOCK.readLock().unlock();
-    }
-
-  }
-
-  /**
-   * Adds the specified column range to the hidden columns
+   * Adds the specified column range to the hidden columns collection
    * 
    * @param start
+   *          start of range to add (absolute position in alignment)
    * @param end
+   *          end of range to add (absolute position in alignment)
    */
   public void hideColumns(int start, int end)
   {
@@ -547,53 +492,7 @@ public class HiddenColumns
         boolean added = false;
         for (int i = 0; !added && i < hiddenColumns.size(); i++)
         {
-          int[] region = hiddenColumns.get(i);
-
-          if (end < region[0] - 1)
-          {
-            /*
-             * insert discontiguous preceding range
-             */
-            hiddenColumns.add(i, new int[] { start, end });
-            added = true;
-          }
-          else if (end <= region[1])
-          {
-            /*
-             * new range overlaps existing, or is contiguous preceding it - adjust
-             * start column
-             */
-            region[0] = Math.min(region[0], start);
-            added = true;
-          }
-          else if (start <= region[1] + 1)
-          {
-            /*
-             * new range overlaps existing, or is contiguous following it - adjust
-             * start and end columns
-             */
-            region[0] = Math.min(region[0], start);
-            region[1] = Math.max(region[1], end);
-
-            /*
-             * also update or remove any subsequent ranges 
-             * that are overlapped
-             */
-            while (i < hiddenColumns.size() - 1)
-            {
-              int[] nextRegion = hiddenColumns.get(i + 1);
-              if (nextRegion[0] > end + 1)
-              {
-                /*
-                 * gap to next hidden range - no more to update
-                 */
-                break;
-              }
-              region[1] = Math.max(nextRegion[1], end);
-              hiddenColumns.remove(i + 1);
-            }
-            added = true;
-          }
+          added = insertRangeAtRegion(i, start, end);
         } // for
       }
     } finally
@@ -605,13 +504,73 @@ public class HiddenColumns
     }
   }
 
+  private boolean insertRangeAtRegion(int i, int start, int end)
+  {
+    boolean added = false;
+
+    int[] region = hiddenColumns.get(i);
+    if (end < region[0] - 1)
+    {
+      /*
+       * insert discontiguous preceding range
+       */
+      hiddenColumns.add(i, new int[] { start, end });
+      added = true;
+    }
+    else if (end <= region[1])
+    {
+      /*
+       * new range overlaps existing, or is contiguous preceding it - adjust
+       * start column
+       */
+      region[0] = Math.min(region[0], start);
+      added = true;
+    }
+    else if (start <= region[1] + 1)
+    {
+      /*
+       * new range overlaps existing, or is contiguous following it - adjust
+       * start and end columns
+       */
+      region[0] = Math.min(region[0], start);
+      region[1] = Math.max(region[1], end);
+
+      /*
+       * also update or remove any subsequent ranges 
+       * that are overlapped
+       */
+      while (i < hiddenColumns.size() - 1)
+      {
+        int[] nextRegion = hiddenColumns.get(i + 1);
+        if (nextRegion[0] > end + 1)
+        {
+          /*
+           * gap to next hidden range - no more to update
+           */
+          break;
+        }
+        region[1] = Math.max(nextRegion[1], end);
+        hiddenColumns.remove(i + 1);
+      }
+      added = true;
+    }
+    return added;
+  }
+
+  /**
+   * Answers if a column in the alignment is visible
+   * 
+   * @param column
+   *          absolute position of column in the alignment
+   * @return true if column is visible
+   */
   public boolean isVisible(int column)
   {
     try
     {
       LOCK.readLock().lock();
 
-      Iterator<int[]> it = new LocalBoundedHiddenColsIterator();
+      Iterator<int[]> it = new RegionsIterator();
       while (it.hasNext())
       {
         int[] region = it.next();
@@ -628,31 +587,17 @@ public class HiddenColumns
     }
   }
 
-  private ArrayList<int[]> copyHiddenRegionsToArrayList(int startIndex)
-  {
-    int size = 0;
-    if (hiddenColumns != null)
-    {
-      size = hiddenColumns.size();
-    }
-    ArrayList<int[]> copy = new ArrayList<>(size);
-
-    for (int i = startIndex, j = size; i < j; i++)
-    {
-      int[] rh;
-      int[] cp;
-      rh = hiddenColumns.get(i);
-      if (rh != null)
-      {
-        cp = new int[rh.length];
-        System.arraycopy(rh, 0, cp, 0, rh.length);
-        copy.add(cp);
-      }
-    }
-
-    return copy;
-  }
-
+  /**
+   * Get the visible sections of a set of sequences
+   * 
+   * @param start
+   *          sequence position to start from
+   * @param end
+   *          sequence position to end at
+   * @param seqs
+   *          an array of sequences
+   * @return an array of strings encoding the visible parts of each sequence
+   */
   public String[] getVisibleSequenceStrings(int start, int end,
           SequenceI[] seqs)
   {
@@ -725,7 +670,7 @@ public class HiddenColumns
 
       // Simply walk along the sequence whilst watching for hidden column
       // boundaries
-      Iterator<int[]> regions = new LocalBoundedHiddenColsIterator();
+      Iterator<int[]> regions = new RegionsIterator();
       int hideStart = seq.getLength();
       int hideEnd = -1;
       int visPrev = 0;
@@ -825,7 +770,7 @@ public class HiddenColumns
           AlignmentAnnotation alignmentAnnotation)
   {
     // mangle the alignmentAnnotation annotation array
-    Vector<Annotation[]> annels = new Vector<>();
+    ArrayList<Annotation[]> annels = new ArrayList<>();
     Annotation[] els = null;
 
     int w = 0;
@@ -860,7 +805,7 @@ public class HiddenColumns
       }
       
       els = new Annotation[annotationLength];
-      annels.addElement(els);
+      annels.add(els);
       System.arraycopy(alignmentAnnotation.annotations, block[0], els, 0,
               copylength);
       w += annotationLength;
@@ -942,7 +887,7 @@ public class HiddenColumns
     try
     {
       LOCK.writeLock().lock();
-      Iterator<int[]> it = new LocalBoundedHiddenColsIterator();
+      Iterator<int[]> it = new RegionsIterator();
       while (it.hasNext())
       {
         int[] region = it.next();
@@ -970,7 +915,7 @@ public class HiddenColumns
     try
     {
       LOCK.writeLock().lock();
-      Iterator<int[]> it = new LocalBoundedHiddenColsIterator();
+      Iterator<int[]> it = new RegionsIterator();
       while (it.hasNext())
       {
         int[] region = it.next();
@@ -1064,7 +1009,7 @@ public class HiddenColumns
       // of
       // preceding visible gaps
       // update hidden columns at the same time
-      Iterator<int[]> regions = new LocalBoundedHiddenColsIterator();
+      Iterator<int[]> regions = new RegionsIterator();
       ArrayList<int[]> newhidden = new ArrayList<>();
 
       int numGapsBefore = 0;
@@ -1169,7 +1114,7 @@ public class HiddenColumns
     {
       LOCK.readLock().lock();
       int hashCode = 1;
-      Iterator<int[]> it = new LocalBoundedHiddenColsIterator();
+      Iterator<int[]> it = new RegionsIterator();
       while (it.hasNext())
       {
         int[] hidden = it.next();
@@ -1221,7 +1166,7 @@ public class HiddenColumns
       {
         return;
       }
-      Iterator<int[]> it = new LocalBoundedHiddenColsIterator();
+      Iterator<int[]> it = new RegionsIterator();
       while (it.hasNext())
       {
         int[] range = it.next();
@@ -1257,7 +1202,7 @@ public class HiddenColumns
         return new int[] { startPos, endPos };
       }
 
-      Iterator<int[]> it = new LocalBoundedHiddenColsIterator();
+      Iterator<int[]> it = new RegionsIterator();
       while (it.hasNext())
       {
         int[] range = it.next();
@@ -1305,7 +1250,7 @@ public class HiddenColumns
       int adjres = adjustForHiddenColumns(res);
 
       int[] reveal = null;
-      Iterator<int[]> it = new LocalBoundedHiddenColsIterator(adjres - 2,
+      Iterator<int[]> it = new RegionsIterator(adjres - 2,
               adjres + 2);
       while (it.hasNext())
       {
@@ -1421,14 +1366,17 @@ public class HiddenColumns
 
   /**
    * A local iterator which iterates over hidden column regions in a range.
-   * Works with the actual hidden columns collection (so locking before use may
-   * be required).
+   * Intended for use ONLY within the HiddenColumns class, because it works
+   * directly with the hiddenColumns collection without locking (callers should
+   * lock hiddenColumns).
    */
-  private class LocalBoundedHiddenColsIterator implements Iterator<int[]>
+  private class RegionsIterator implements Iterator<int[]>
   {
-    private int start; // start position to iterate from
+    // start position to iterate from
+    private int start;
 
-    private int end; // end position to iterate to
+    // end position to iterate to
+    private int end;
 
     // current index in hiddenColumns
     private int currentPosition = 0;
@@ -1436,20 +1384,24 @@ public class HiddenColumns
     // current column in hiddenColumns
     private int[] nextRegion = null;
 
-    LocalBoundedHiddenColsIterator(int lowerBound, int upperBound)
+    // Constructor with bounds
+    RegionsIterator(int lowerBound, int upperBound)
     {
       init(lowerBound, upperBound);
     }
 
-    LocalBoundedHiddenColsIterator()
+    // Unbounded constructor
+    RegionsIterator()
     {
       if (hiddenColumns != null)
       {
+        // iterator over full hiddenColumns collection
         int last = hiddenColumns.get(hiddenColumns.size() - 1)[1];
         init(0, last);
       }
       else
       {
+        // empty iterator
         init(0, 0);
       }
     }
@@ -1462,9 +1414,6 @@ public class HiddenColumns
      *          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)
      */
     private void init(int lowerBound, int upperBound)
     {
@@ -1513,14 +1462,98 @@ public class HiddenColumns
   }
 
   /**
+   * 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.
+   * with a copy of the hidden columns collection. Intended to be used by
+   * callers OUTSIDE of HiddenColumns.
    */
   private class BoundedHiddenColsIterator implements Iterator<int[]>
   {
-    private int start; // start position to iterate from
+    // start position to iterate from
+    private int start;
 
-    private int end; // end position to iterate to
+    // end position to iterate to
+    private int end;
 
     // current index in hiddenColumns
     private int currentPosition = 0;
@@ -1531,6 +1564,9 @@ public class HiddenColumns
     // local copy or reference to hiddenColumns
     private List<int[]> localHidden;
 
+    /**
+     * Unbounded constructor
+     */
     BoundedHiddenColsIterator()
     {
       if (hiddenColumns != null)