X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;ds=inline;f=src%2Fjalview%2Fdatamodel%2FHiddenColumns.java;h=9f3b9299bb8a8bd15d228596c79d44078faf496b;hb=a6789f8a91ca11fac840b958ee9207581ea4d14f;hp=169b0a4e4dca973dd5da9abd0d5ef1204243758a;hpb=834dcc21a8a324b092285c1295df9392654d526b;p=jalview.git diff --git a/src/jalview/datamodel/HiddenColumns.java b/src/jalview/datamodel/HiddenColumns.java index 169b0a4..9f3b929 100644 --- a/src/jalview/datamodel/HiddenColumns.java +++ b/src/jalview/datamodel/HiddenColumns.java @@ -21,19 +21,21 @@ package jalview.datamodel; import jalview.util.Comparison; -import jalview.util.ShiftList; import java.util.ArrayList; import java.util.BitSet; import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.Vector; import java.util.concurrent.locks.ReentrantReadWriteLock; public class HiddenColumns { + private static final int HASH_MULTIPLIER = 31; + private static final ReentrantReadWriteLock LOCK = new ReentrantReadWriteLock(); - + /* * list of hidden column [start, end] ranges; the list is maintained in * ascending start column order @@ -61,7 +63,7 @@ public class HiddenColumns { if (copy.hiddenColumns != null) { - hiddenColumns = copy.copyHiddenRegionsToArrayList(); + hiddenColumns = copy.copyHiddenRegionsToArrayList(0); } } } finally @@ -135,8 +137,7 @@ public class HiddenColumns } } return size; - } - finally + } finally { LOCK.readLock().unlock(); } @@ -290,47 +291,47 @@ public class HiddenColumns { LOCK.readLock().lock(); - int distance = visibleDistance; + int distance = visibleDistance; - // in case startColumn is in a hidden region, move it to the left - int start = adjustForHiddenColumns(findColumnPosition(startColumn)); + // 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; - } + // 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; + // walk backwards through the alignment subtracting the counts of visible + // columns from distance + int[] region; + int gap = 0; + int nextstart = start; - while ((index > -1) && (distance - gap > 0)) - { - // subtract the gap to right of region from distance - distance -= gap; - start = nextstart; + while ((index > -1) && (distance - gap > 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]; + // calculate the next gap + region = hiddenColumns.get(index); + gap = start - region[1]; - // set start to just to left of current region - nextstart = region[0] - 1; - index--; - } + // set start to just to left of current region + nextstart = region[0] - 1; + index--; + } - if (distance - gap > 0) - { - // fell out of loop because there are no more hidden regions - distance -= gap; - return nextstart - distance; - } - return start - distance; + 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(); @@ -340,10 +341,16 @@ public class HiddenColumns /** * Use this method to determine the set of hiddenRegion start positions + * between absolute position and absolute position * - * @return list of column number in visible view where hidden regions start + * @param start + * absolute residue to start from + * @param end + * absolute residue to end at + * + * @return list of column numbers in *visible* view where hidden regions start */ - public List findHiddenRegionPositions() + public List findHiddenRegionPositions(int start, int end) { try { @@ -354,26 +361,26 @@ public class HiddenColumns { positions = new ArrayList<>(hiddenColumns.size()); - positions.add(hiddenColumns.get(0)[0]); - for (int i = 1; i < hiddenColumns.size(); ++i) + // navigate to start, keeping count of hidden columns + int i = 0; + int hiddenSoFar = 0; + while ((i < hiddenColumns.size()) + && (hiddenColumns.get(i)[0] < start)) { + int[] region = hiddenColumns.get(i); + hiddenSoFar += region[1] - region[0] + 1; + i++; + } - int result = 0; - if (hiddenColumns != null) - { - int index = 0; - int gaps = 0; - do - { - int[] region = hiddenColumns.get(index); - gaps += region[1] + 1 - region[0]; - result = region[1] + 1; - index++; - } while (index <= i); - - result -= gaps; - } - positions.add(result); + // 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)) + { + int[] region = hiddenColumns.get(i); + positions.add(region[0] - hiddenSoFar); + hiddenSoFar += region[1] - region[0] + 1; + i++; } } else @@ -382,8 +389,7 @@ public class HiddenColumns } return positions; - } - finally + } finally { LOCK.readLock().unlock(); } @@ -437,22 +443,22 @@ public class HiddenColumns { LOCK.readLock().lock(); - if (hiddenColumns != null) - { - int index = hiddenColumns.size() - 1; - do + if (hiddenColumns != null) { - int[] region = hiddenColumns.get(index); - if (alPos > region[1]) + int index = hiddenColumns.size() - 1; + do { - return region[1]; - } + int[] region = hiddenColumns.get(index); + if (alPos > region[1]) + { + return region[1]; + } - index--; - } while (index > -1); - } + index--; + } while (index > -1); + } - return alPos; + return alPos; } finally { LOCK.readLock().unlock(); @@ -473,22 +479,22 @@ public class HiddenColumns { LOCK.readLock().lock(); - if (hiddenColumns != null) - { - int index = hiddenColumns.size() - 1; - do + if (hiddenColumns != null) { - int[] region = hiddenColumns.get(index); - if (pos > region[1]) + int index = hiddenColumns.size() - 1; + do { - return index; - } + int[] region = hiddenColumns.get(index); + if (pos > region[1]) + { + return index; + } - index--; - } while (index > -1); - } + index--; + } while (index > -1); + } - return -1; + return -1; } finally { LOCK.readLock().unlock(); @@ -578,11 +584,11 @@ public class HiddenColumns } return; } - } + } - /* - * remaining case is that the new range follows everything else - */ + /* + * remaining case is that the new range follows everything else + */ hiddenColumns.add(new int[] { start, end }); } finally { @@ -599,25 +605,25 @@ public class HiddenColumns { LOCK.readLock().lock(); - if (hiddenColumns != null) - { - for (int[] region : hiddenColumns) + if (hiddenColumns != null) { - if (column >= region[0] && column <= region[1]) + for (int[] region : hiddenColumns) { - return false; + if (column >= region[0] && column <= region[1]) + { + return false; + } } } - } - return true; + return true; } finally { LOCK.readLock().unlock(); } } - private ArrayList copyHiddenRegionsToArrayList() + private ArrayList copyHiddenRegionsToArrayList(int startIndex) { int size = 0; if (hiddenColumns != null) @@ -626,7 +632,7 @@ public class HiddenColumns } ArrayList copy = new ArrayList<>(size); - for (int i = 0, j = size; i < j; i++) + for (int i = startIndex, j = size; i < j; i++) { int[] rh; int[] cp; @@ -641,7 +647,7 @@ public class HiddenColumns return copy; } - + /** * 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 @@ -655,7 +661,7 @@ public class HiddenColumns try { LOCK.readLock().lock(); - return copyHiddenRegionsToArrayList(); + return copyHiddenRegionsToArrayList(0); } finally { LOCK.readLock().unlock(); @@ -663,111 +669,6 @@ public class HiddenColumns } /** - * propagate shift in alignment columns to column selection - * - * @param start - * beginning of edit - * @param left - * shift in edit (+ve for removal, or -ve for inserts) - */ - public List compensateForEdit(int start, int change, - ColumnSelection sel) - { - try - { - LOCK.writeLock().lock(); - List deletedHiddenColumns = null; - - if (hiddenColumns != null) - { - deletedHiddenColumns = new ArrayList<>(); - int hSize = hiddenColumns.size(); - for (int i = 0; i < hSize; i++) - { - int[] region = hiddenColumns.get(i); - if (region[0] > start && start + change > region[1]) - { - deletedHiddenColumns.add(region); - - hiddenColumns.remove(i); - i--; - hSize--; - continue; - } - - if (region[0] > start) - { - region[0] -= change; - region[1] -= change; - } - - if (region[0] < 0) - { - region[0] = 0; - } - - } - - this.revealHiddenColumns(0, sel); - } - - return deletedHiddenColumns; - } finally - { - LOCK.writeLock().unlock(); - } - } - - /** - * propagate shift in alignment columns to column selection special version of - * compensateForEdit - allowing for edits within hidden regions - * - * @param start - * beginning of edit - * @param left - * shift in edit (+ve for removal, or -ve for inserts) - */ - public void compensateForDelEdits(int start, int change) - { - try - { - LOCK.writeLock().lock(); - if (hiddenColumns != null) - { - for (int i = 0; i < hiddenColumns.size(); i++) - { - int[] region = hiddenColumns.get(i); - if (region[0] >= start) - { - region[0] -= change; - } - if (region[1] >= start) - { - region[1] -= change; - } - if (region[1] < region[0]) - { - hiddenColumns.remove(i--); - } - - if (region[0] < 0) - { - region[0] = 0; - } - if (region[1] < 0) - { - region[1] = 0; - } - } - } - } - finally - { - LOCK.writeLock().unlock(); - } - } - - /** * return all visible segments between the given start and end boundaries * * @param start @@ -784,52 +685,62 @@ public class HiddenColumns LOCK.readLock().lock(); if (hiddenColumns != null && hiddenColumns.size() > 0) { - List visiblecontigs = new ArrayList<>(); - List regions = getHiddenRegions(); - + // 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[] region; int hideStart; int hideEnd; + int i = 0; - for (int j = 0; vstart < end && j < regions.size(); j++) + for (int[] region : hiddenColumns) { - region = regions.get(j); hideStart = region[0]; hideEnd = region[1]; + // navigate to start if (hideEnd < vstart) { continue; } if (hideStart > vstart) { - visiblecontigs.add(new int[] { vstart, hideStart - 1 }); + vcontigs[i * 2] = vstart; + vcontigs[i * 2 + 1] = hideStart - 1; + i++; } vstart = hideEnd + 1; + + // exit if we're past the end + if (vstart >= end) + { + break; + } } if (vstart < end) { - visiblecontigs.add(new int[] { vstart, end - 1 }); - } - int[] vcontigs = new int[visiblecontigs.size() * 2]; - for (int i = 0, j = visiblecontigs.size(); i < j; i++) - { - int[] vc = visiblecontigs.get(i); - visiblecontigs.set(i, null); - vcontigs[i * 2] = vc[0]; - vcontigs[i * 2 + 1] = vc[1]; + vcontigs[i * 2] = vstart; + vcontigs[i * 2 + 1] = end - 1; + i++; } - visiblecontigs.clear(); - return vcontigs; + + // 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 }; } - } - finally + } finally { LOCK.readLock().unlock(); } @@ -848,17 +759,14 @@ public class HiddenColumns for (int i = 0; i < iSize; i++) { StringBuffer visibleSeq = new StringBuffer(); - List regions = getHiddenRegions(); int blockStart = start; int blockEnd = end; - int[] region; int hideStart; int hideEnd; - for (int j = 0; j < regions.size(); j++) + for (int[] region : hiddenColumns) { - region = regions.get(j); hideStart = region[0]; hideEnd = region[1]; @@ -898,8 +806,7 @@ public class HiddenColumns } return selections; - } - finally + } finally { LOCK.readLock().unlock(); } @@ -928,14 +835,13 @@ public class HiddenColumns { int ifpos = seq.findIndex(fpos) - 1; int ilpos = seq.findIndex(lpos) - 1; - return new int[] { ifpos, ilpos, fpos, lpos, ifpos, ilpos }; + return new int[] { ifpos, ifpos, ilpos }; } // Simply walk along the sequence whilst watching for hidden column // boundaries List regions = getHiddenRegions(); int spos = fpos; - int lastvispos = -1; int rcount = 0; int hideStart = seq.getLength(); int hideEnd = -1; @@ -978,7 +884,6 @@ public class HiddenColumns start = p; foundStart = true; } - lastvispos = p; lpos = spos; } // look for next sequence position @@ -987,13 +892,11 @@ public class HiddenColumns } if (foundStart) { - return new int[] { findColumnPosition(start), - findColumnPosition(lastvispos), fpos, lpos, firstP, lastP }; + return new int[] { findColumnPosition(start), firstP, lastP }; } // otherwise, sequence was completely hidden - return new int[] { visPrev, visNext, 0, 0, firstP, lastP }; - } - finally + return new int[] { visPrev, firstP, lastP }; + } finally { LOCK.readLock().unlock(); } @@ -1041,17 +944,14 @@ public class HiddenColumns // then mangle the alignmentAnnotation annotation array Vector annels = new Vector<>(); Annotation[] els = null; - List regions = getHiddenRegions(); int blockStart = start; int blockEnd = end; - int[] region; int hideStart; int hideEnd; int w = 0; - for (int j = 0; j < regions.size(); j++) + for (int[] region : hiddenColumns) { - region = regions.get(j); hideStart = region[0]; hideEnd = region[1]; @@ -1068,7 +968,8 @@ public class HiddenColumns break; } - annels.addElement(els = new Annotation[blockEnd - blockStart]); + els = new Annotation[blockEnd - blockStart]; + annels.addElement(els); System.arraycopy(alignmentAnnotation.annotations, blockStart, els, 0, els.length); w += els.length; @@ -1078,7 +979,8 @@ public class HiddenColumns if (end > blockStart) { - annels.addElement(els = new Annotation[end - blockStart + 1]); + els = new Annotation[end - blockStart + 1]; + annels.addElement(els); if ((els.length + blockStart) <= alignmentAnnotation.annotations.length) { @@ -1114,8 +1016,7 @@ public class HiddenColumns { alignmentAnnotation.restrict(start, end); } - } - finally + } finally { LOCK.readLock().unlock(); } @@ -1196,8 +1097,7 @@ public class HiddenColumns } hiddenColumns = null; - } - finally + } finally { LOCK.writeLock().unlock(); } @@ -1232,133 +1132,7 @@ public class HiddenColumns { hiddenColumns = null; } - } - finally - { - LOCK.writeLock().unlock(); - } - } - - /** - * removes intersection of position,length ranges in deletions from the - * start,end regions marked in intervals. - * - * @param shifts - * @param intervals - * @return - */ - private boolean pruneIntervalList(final List shifts, - ArrayList intervals) - { - boolean pruned = false; - int i = 0; - int j = intervals.size() - 1; - int s = 0; - int t = shifts.size() - 1; - int[] hr = intervals.get(i); - int[] sr = shifts.get(s); - while (i <= j && s <= t) - { - boolean trailinghn = hr[1] >= sr[0]; - if (!trailinghn) - { - if (i < j) - { - hr = intervals.get(++i); - } - else - { - i++; - } - continue; - } - int endshift = sr[0] + sr[1]; // deletion ranges - -ve means an insert - if (endshift < hr[0] || endshift < sr[0]) - { // leadinghc disjoint or not a deletion - if (s < t) - { - sr = shifts.get(++s); - } - else - { - s++; - } - continue; - } - boolean leadinghn = hr[0] >= sr[0]; - boolean leadinghc = hr[0] < endshift; - boolean trailinghc = hr[1] < endshift; - if (leadinghn) - { - if (trailinghc) - { // deleted hidden region. - intervals.remove(i); - pruned = true; - j--; - if (i <= j) - { - hr = intervals.get(i); - } - continue; - } - if (leadinghc) - { - hr[0] = endshift; // clip c terminal region - leadinghn = !leadinghn; - pruned = true; - } - } - if (!leadinghn) - { - if (trailinghc) - { - if (trailinghn) - { - hr[1] = sr[0] - 1; - pruned = true; - } - } - else - { - // sr contained in hr - if (s < t) - { - sr = shifts.get(++s); - } - else - { - s++; - } - continue; - } - } - } - return pruned; // true if any interval was removed or modified by - // operations. - } - - /** - * remove any hiddenColumns or selected columns and shift remaining based on a - * series of position, range deletions. - * - * @param deletions - */ - public void pruneDeletions(List shifts) - { - try - { - LOCK.writeLock().lock(); - // delete any intervals intersecting. - if (hiddenColumns != null) - { - pruneIntervalList(shifts, hiddenColumns); - if (hiddenColumns != null && hiddenColumns.size() == 0) - { - hiddenColumns = null; - } - } - } - finally + } finally { LOCK.writeLock().unlock(); } @@ -1404,153 +1178,113 @@ public class HiddenColumns SequenceI origseq) { char gc = al.getGapCharacter(); - // recover mapping between sequence's non-gap positions and positions - // mapping to view. - pruneDeletions(ShiftList.parseMap(origseq.gapMap())); - int[] viscontigs = getVisibleContigs(0, profileseq.getLength()); - int spos = 0; - int offset = 0; - // add profile to visible contigs - for (int v = 0; v < viscontigs.length; v += 2) - { - if (viscontigs[v] > spos) + // take the set of hidden columns, and the set of gaps in origseq, + // and remove all the hidden gaps from hiddenColumns + + // first get the gaps as a Bitset + BitSet gaps = origseq.gapBitset(); + + // now calculate hidden ^ not(gap) + BitSet hidden = new BitSet(); + markHiddenRegions(hidden); + hidden.andNot(gaps); + hiddenColumns = null; + this.hideMarkedBits(hidden); + + // for each sequence in the alignment, except the profile sequence, + // insert gaps corresponding to each hidden region + // but where each hidden column region is shifted backwards by the number of + // preceding visible gaps + // update hidden columns at the same time + ArrayList regions = getHiddenColumnsCopy(); + ArrayList newhidden = new ArrayList<>(); + + int numGapsBefore = 0; + int gapPosition = 0; + for (int[] region : regions) + { + // get region coordinates accounting for gaps + // we can rely on gaps not being *in* hidden regions because we already + // removed those + while (gapPosition < region[0]) { - StringBuffer sb = new StringBuffer(); - for (int s = 0, ns = viscontigs[v] - spos; s < ns; s++) + gapPosition++; + if (gaps.get(gapPosition)) { - sb.append(gc); + numGapsBefore++; } - for (int s = 0, ns = al.getHeight(); s < ns; s++) - { - SequenceI sqobj = al.getSequenceAt(s); - if (sqobj != profileseq) - { - String sq = al.getSequenceAt(s).getSequenceAsString(); - if (sq.length() <= spos + offset) - { - // pad sequence - int diff = spos + offset - sq.length() - 1; - if (diff > 0) - { - // pad gaps - sq = sq + sb; - while ((diff = spos + offset - sq.length() - 1) > 0) - { - // sq = sq - // + ((diff >= sb.length()) ? sb.toString() : sb - // .substring(0, diff)); - if (diff >= sb.length()) - { - sq += sb.toString(); - } - else - { - char[] buf = new char[diff]; - sb.getChars(0, diff, buf, 0); - sq += buf.toString(); - } - } - } - sq += sb.toString(); - } - else - { - al.getSequenceAt(s).setSequence( - sq.substring(0, spos + offset) + sb.toString() - + sq.substring(spos + offset)); - } - } - } - // offset+=sb.length(); } - spos = viscontigs[v + 1] + 1; - } - if ((offset + spos) < profileseq.getLength()) - { - // pad the final region with gaps. + + int left = region[0] - numGapsBefore; + int right = region[1] - numGapsBefore; + newhidden.add(new int[] { left, right }); + + // make a string with number of gaps = length of hidden region StringBuffer sb = new StringBuffer(); - for (int s = 0, ns = profileseq.getLength() - spos - offset; s < ns; s++) + for (int s = 0; s < right - left + 1; s++) { sb.append(gc); } - for (int s = 0, ns = al.getHeight(); s < ns; s++) - { - SequenceI sqobj = al.getSequenceAt(s); - if (sqobj == profileseq) - { - continue; - } - String sq = sqobj.getSequenceAsString(); - // pad sequence - int diff = origseq.getLength() - sq.length(); - while (diff > 0) - { - // sq = sq - // + ((diff >= sb.length()) ? sb.toString() : sb - // .substring(0, diff)); - if (diff >= sb.length()) - { - sq += sb.toString(); - } - else - { - char[] buf = new char[diff]; - sb.getChars(0, diff, buf, 0); - sq += buf.toString(); - } - diff = origseq.getLength() - sq.length(); - } - } - } - } - - /** - * remove any hiddenColumns or selected columns and shift remaining based on a - * series of position, range deletions. - * - * @param deletions - */ - private void pruneDeletions(ShiftList deletions) - { - if (deletions != null) - { - final List shifts = deletions.getShifts(); - if (shifts != null && shifts.size() > 0) - { - pruneDeletions(shifts); + padGaps(sb, left, profileseq, al); - // and shift the rest. - this.compensateForEdits(deletions); - } } + hiddenColumns = newhidden; } /** - * Adjust hidden column boundaries based on a series of column additions or - * deletions in visible regions. + * Pad gaps in all sequences in alignment except profileseq * - * @param shiftrecord - * @return + * @param sb + * gap string to insert + * @param left + * position to insert at + * @param profileseq + * sequence not to pad + * @param al + * alignment to pad sequences in */ - private ShiftList compensateForEdits(ShiftList shiftrecord) + private void padGaps(StringBuffer sb, int pos, SequenceI profileseq, + AlignmentI al) { - if (shiftrecord != null) + // loop over the sequences and pad with gaps where required + for (int s = 0, ns = al.getHeight(); s < ns; s++) { - final List shifts = shiftrecord.getShifts(); - if (shifts != null && shifts.size() > 0) + SequenceI sqobj = al.getSequenceAt(s); + if (sqobj != profileseq) { - int shifted = 0; - for (int i = 0, j = shifts.size(); i < j; i++) + String sq = al.getSequenceAt(s).getSequenceAsString(); + if (sq.length() <= pos) { - int[] sh = shifts.get(i); - compensateForDelEdits(shifted + sh[0], sh[1]); - shifted -= sh[1]; + // pad sequence + int diff = pos - sq.length() - 1; + if (diff > 0) + { + // pad gaps + sq = sq + sb; + while ((diff = pos - sq.length() - 1) > 0) + { + if (diff >= sb.length()) + { + sq += sb.toString(); + } + else + { + char[] buf = new char[diff]; + sb.getChars(0, diff, buf, 0); + sq += buf.toString(); + } + } + } + sq += sb.toString(); + } + else + { + al.getSequenceAt(s).setSequence( + sq.substring(0, pos) + sb.toString() + sq.substring(pos)); } } - return shiftrecord.getInverse(); } - return null; } /** @@ -1567,13 +1301,12 @@ public class HiddenColumns { for (int[] hidden : hiddenColumns) { - hashCode = 31 * hashCode + hidden[0]; - hashCode = 31 * hashCode + hidden[1]; + hashCode = HASH_MULTIPLIER * hashCode + hidden[0]; + hashCode = HASH_MULTIPLIER * hashCode + hidden[1]; } } return hashCode; - } - finally + } finally { LOCK.readLock().unlock(); } @@ -1621,8 +1354,7 @@ public class HiddenColumns { inserts.set(range[0], range[1] + 1); } - } - finally + } finally { LOCK.readLock().unlock(); } @@ -1716,4 +1448,226 @@ public class HiddenColumns } } + public Iterator getBoundedIterator(int start, int end, + boolean useCopy) + { + return new BoundedHiddenColsIterator(start, end, useCopy); + } + + public Iterator getBoundedStartIterator(int start, int end, + boolean useCopy) + { + return new BoundedStartRegionIterator(start, end, useCopy); + } + + /** + * An iterator which iterates over hidden column regions in a range. + * + * @author kmourao + * + */ + + + class BoundedHiddenColsIterator implements Iterator + { + + 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 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 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) + */ + 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 + { + + 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 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; + } + } }