+ /**
+ * An iterator which iterates over visible regions in a range.
+ */
+ private class VisibleContigsIterator implements Iterator<int[]>
+ {
+ private List<int[]> vcontigs = new ArrayList<>();
+
+ private int currentPosition = 0;
+
+ VisibleContigsIterator(int start, int end, boolean usecopy)
+ {
+ try
+ {
+ if (usecopy)
+ {
+ LOCK.readLock().lock();
+ }
+
+ if (hiddenColumns != null && hiddenColumns.size() > 0)
+ {
+ int vstart = start;
+ int hideStart;
+ int hideEnd;
+
+ for (int[] region : hiddenColumns)
+ {
+ hideStart = region[0];
+ hideEnd = region[1];
+
+ // navigate to start
+ if (hideEnd < vstart)
+ {
+ continue;
+ }
+ if (hideStart > vstart)
+ {
+ int[] contig = new int[] { vstart, hideStart - 1 };
+ vcontigs.add(contig);
+ }
+ vstart = hideEnd + 1;
+
+ // exit if we're past the end
+ if (vstart >= end)
+ {
+ break;
+ }
+ }
+
+ if (vstart < end)
+ {
+ int[] contig = new int[] { vstart, end - 1 };
+ vcontigs.add(contig);
+ }
+ }
+ else
+ {
+ int[] contig = new int[] { start, end - 1 };
+ vcontigs.add(contig);
+ }
+ } finally
+ {
+ if (usecopy)
+ {
+ LOCK.readLock().unlock();
+ }
+ }
+ }
+
+ @Override
+ public boolean hasNext()
+ {
+ return (currentPosition < vcontigs.size());
+ }
+
+ @Override
+ public int[] next()
+ {
+ int[] result = vcontigs.get(currentPosition);
+ currentPosition++;
+ return result;
+ }
+ }
+
+ /**
+ * An iterator which iterates over visible regions in a range. The range is
+ * specified in terms of visible column positions. Provides a special
+ * "endsAtHidden" indicator to allow callers to determine if the final visible
+ * column is adjacent to a hidden region.
+ */
+ public class VisibleBlocksVisBoundsIterator implements Iterator<int[]>
+ {
+ private List<int[]> vcontigs = new ArrayList<>();
+
+ private int currentPosition = 0;
+
+ private boolean endsAtHidden = false;
+
+ /**
+ * Constructor for iterator over visible regions in a range.
+ *
+ * @param start
+ * start position in terms of visible column position
+ * @param end
+ * end position in terms of visible column position
+ * @param usecopy
+ * whether to use a local copy of hidden columns
+ */
+ VisibleBlocksVisBoundsIterator(int start, int end, boolean usecopy)
+ {
+ /* actually this implementation always uses a local copy but this may change in future */
+ try
+ {
+ if (usecopy)
+ {
+ LOCK.readLock().lock();
+ }
+
+ if (hiddenColumns != null && hiddenColumns.size() > 0)
+ {
+ int blockStart = start;
+ int blockEnd = end;
+ int hiddenSoFar = 0;
+ int visSoFar = 0;
+
+ // iterate until a region begins within (start,end]
+ int i = 0;
+ while ((i < hiddenColumns.size())
+ && (hiddenColumns.get(i)[0] <= blockStart + hiddenSoFar))
+ {
+ hiddenSoFar += hiddenColumns.get(i)[1] - hiddenColumns.get(i)[0]
+ + 1;
+ i++;
+ }
+
+ blockStart += hiddenSoFar; // convert start to absolute position
+ blockEnd += hiddenSoFar; // convert end to absolute position
+
+ // iterate from start to end, adding each visible region. Positions
+ // are
+ // absolute, and all hidden regions which overlap [start,end] are
+ // used.
+ while (i < hiddenColumns.size()
+ && (hiddenColumns.get(i)[0] <= blockEnd))
+ {
+ int[] region = hiddenColumns.get(i);
+
+ // end position of this visible region is either just before the
+ // start of the next hidden region, or the absolute position of
+ // 'end', whichever is lowest
+ blockEnd = Math.min(blockEnd, region[0] - 1);
+
+ vcontigs.add(new int[] { blockStart, blockEnd });
+
+ visSoFar += blockEnd - blockStart + 1;
+
+ // next visible region starts after this hidden region
+ blockStart = region[1] + 1;
+
+ hiddenSoFar += region[1] - region[0] + 1;
+
+ // reset blockEnd to absolute position of 'end', assuming we've now
+ // passed all hidden regions before end
+ blockEnd = end + hiddenSoFar;
+
+ i++;
+ }
+ if (visSoFar < end - start)
+ {
+ // the number of visible columns we've accounted for is less than
+ // the number specified by end-start; work out the end position of
+ // the last visible region
+ blockEnd = blockStart + end - start - visSoFar;
+ vcontigs.add(new int[] { blockStart, blockEnd });
+
+ // if the last visible region ends at the next hidden region, set
+ // endsAtHidden=true
+ if (i < hiddenColumns.size()
+ && hiddenColumns.get(i)[0] - 1 == blockEnd)
+ {
+ endsAtHidden = true;
+ }
+ }
+ }
+ else
+ {
+ // there are no hidden columns, return a single visible contig
+ vcontigs.add(new int[] { start, end });
+ endsAtHidden = false;
+ }
+ } finally
+ {
+ if (usecopy)
+ {
+ LOCK.readLock().unlock();
+ }
+ }
+ }
+
+ @Override
+ public boolean hasNext()
+ {
+ return (currentPosition < vcontigs.size());
+ }
+
+ @Override
+ public int[] next()
+ {
+ int[] result = vcontigs.get(currentPosition);
+ currentPosition++;
+ return result;
+ }
+
+ public boolean endsAtHidden()
+ {
+ return endsAtHidden;
+ }
+ }