+ public Iterator<int[]> getBoundedIterator(int start, int end)
+ {
+ 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);
+ }
+
+ /**
+ * 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 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;
+ }
+ }
+
+ 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();
+ }
+ }