From f57d1d3437767e14c2bf711af0e06c13b9d18cd4 Mon Sep 17 00:00:00 2001 From: kiramt Date: Tue, 10 Oct 2017 09:12:45 +0100 Subject: [PATCH] JAL-2759 Rearranged iterators --- .../datamodel/BoundedHiddenColsIterator.java | 111 ++++ .../datamodel/BoundedStartRegionIterator.java | 94 +++ src/jalview/datamodel/HiddenColumns.java | 671 +++----------------- src/jalview/datamodel/HiddenColumnsCursor.java | 186 ++++++ src/jalview/datamodel/RegionsIterator.java | 98 +++ src/jalview/datamodel/ReverseRegionsIterator.java | 75 +++ src/jalview/datamodel/VisibleColsIterator.java | 116 ++++ src/jalview/datamodel/VisibleContigsIterator.java | 76 +++ 8 files changed, 827 insertions(+), 600 deletions(-) create mode 100644 src/jalview/datamodel/BoundedHiddenColsIterator.java create mode 100644 src/jalview/datamodel/BoundedStartRegionIterator.java create mode 100644 src/jalview/datamodel/HiddenColumnsCursor.java create mode 100644 src/jalview/datamodel/RegionsIterator.java create mode 100644 src/jalview/datamodel/ReverseRegionsIterator.java create mode 100644 src/jalview/datamodel/VisibleColsIterator.java create mode 100644 src/jalview/datamodel/VisibleContigsIterator.java diff --git a/src/jalview/datamodel/BoundedHiddenColsIterator.java b/src/jalview/datamodel/BoundedHiddenColsIterator.java new file mode 100644 index 0000000..aa3f4ad --- /dev/null +++ b/src/jalview/datamodel/BoundedHiddenColsIterator.java @@ -0,0 +1,111 @@ +package jalview.datamodel; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * An iterator which iterates over hidden column regions in a range. Works with + * a copy of the hidden columns collection. Intended to be used by callers + * OUTSIDE of HiddenColumns. + */ +public class BoundedHiddenColsIterator implements Iterator +{ + // start position to iterate from + private int start; + + // end position to iterate to + private int end; + + // current index in hiddenColumns + private int currentPosition = 0; + + // current column in hiddenColumns + private int[] currentRegion; + + // local copy or reference to hiddenColumns + private List localHidden; + + /** + * Unbounded constructor + */ + BoundedHiddenColsIterator(List hiddenColumns) + { + if (hiddenColumns != null) + { + int last = hiddenColumns.get(hiddenColumns.size() - 1)[1]; + init(0, last, hiddenColumns); + } + else + { + init(0, 0, hiddenColumns); + } + } + + /** + * Construct an iterator over hiddenColums bounded at [lowerBound,upperBound] + * + * @param lowerBound + * lower bound to iterate from + * @param upperBound + * upper bound to iterate to + */ + BoundedHiddenColsIterator(int lowerBound, int upperBound, + List hiddenColumns) + { + init(lowerBound, upperBound, hiddenColumns); + } + + /** + * Construct an iterator over hiddenColums bounded at [lowerBound,upperBound] + * + * @param lowerBound + * lower bound to iterate from + * @param upperBound + * upper bound to iterate to + */ + private void init(int lowerBound, int upperBound, + List hiddenColumns) + { + start = lowerBound; + end = upperBound; + + 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 = hiddenColumns.get(i); + int[] cp = new int[2]; + System.arraycopy(rh, 0, cp, 0, rh.length); + localHidden.add(cp); + i++; + } + } + } + + @Override + public boolean hasNext() + { + return (localHidden != null) && (currentPosition < localHidden.size()); + } + + @Override + public int[] next() + { + currentRegion = localHidden.get(currentPosition); + currentPosition++; + return currentRegion; + } +} diff --git a/src/jalview/datamodel/BoundedStartRegionIterator.java b/src/jalview/datamodel/BoundedStartRegionIterator.java new file mode 100644 index 0000000..831e906 --- /dev/null +++ b/src/jalview/datamodel/BoundedStartRegionIterator.java @@ -0,0 +1,94 @@ +package jalview.datamodel; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * An iterator which iterates over visible start positions of hidden column + * regions in a range. + */ +public class BoundedStartRegionIterator implements Iterator +{ + // start position to iterate from + private int start; + + // end position to iterate to + private int end; + + // 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, + List hiddenColumns) + { + start = lowerBound; + end = upperBound; + + 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<>(); + } + + } + + @Override + public boolean hasNext() + { + return (currentPosition < positions.size()); + } + + /** + * Get next hidden region start position + * + * @return the start position in *visible* coordinates + */ + @Override + public Integer next() + { + int result = positions.get(currentPosition); + currentPosition++; + return result; + } +} + diff --git a/src/jalview/datamodel/HiddenColumns.java b/src/jalview/datamodel/HiddenColumns.java index 399e083..1ac6015 100644 --- a/src/jalview/datamodel/HiddenColumns.java +++ b/src/jalview/datamodel/HiddenColumns.java @@ -24,7 +24,6 @@ import java.util.ArrayList; import java.util.BitSet; import java.util.Iterator; import java.util.List; -import java.util.NoSuchElementException; import java.util.concurrent.locks.ReentrantReadWriteLock; public class HiddenColumns @@ -33,6 +32,8 @@ public class HiddenColumns private static final ReentrantReadWriteLock LOCK = new ReentrantReadWriteLock(); + private HiddenColumnsCursor cursor = new HiddenColumnsCursor(); + /* * list of hidden column [start, end] ranges; the list is maintained in * ascending start column order @@ -66,6 +67,7 @@ public class HiddenColumns { hiddenColumns.add(it.next()); } + cursor.resetCursor(hiddenColumns); } } } finally @@ -110,6 +112,7 @@ public class HiddenColumns { region[0] - offset, region[1] - offset }); } } + cursor.resetCursor(hiddenColumns); } } finally { @@ -263,6 +266,9 @@ public class HiddenColumns if (hiddenColumns != null) { + result += cursor.getHiddenOffset(column); + + /* Iterator it = hiddenColumns.iterator(); while (it.hasNext()) { @@ -271,7 +277,7 @@ public class HiddenColumns { result += region[1] - region[0] + 1; } - } + }*/ } return result; @@ -300,7 +306,7 @@ public class HiddenColumns if (hiddenColumns != null) { Iterator it = new RegionsIterator(0, - hiddenColumn); + hiddenColumn, hiddenColumns, cursor); while (it.hasNext()) { region = it.next(); @@ -362,7 +368,8 @@ public class HiddenColumns // in case startColumn is in a hidden region, move it to the left int start = adjustForHiddenColumns(findColumnPosition(startColumn)); - Iterator it = new ReverseRegionsIterator(0, start); + Iterator it = new ReverseRegionsIterator(0, start, + hiddenColumns); while (it.hasNext() && (distance > 0)) { @@ -437,7 +444,8 @@ public class HiddenColumns { LOCK.readLock().lock(); - Iterator it = new ReverseRegionsIterator(0, alPos); + Iterator it = new ReverseRegionsIterator(0, alPos, + hiddenColumns); while (it.hasNext()) { int[] region = it.next(); @@ -503,6 +511,10 @@ public class HiddenColumns added = insertRangeAtRegion(i, start, end); } // for } + if (!wasAlreadyLocked) + { + cursor.resetCursor(hiddenColumns); + } } finally { if (!wasAlreadyLocked) @@ -590,7 +602,8 @@ public class HiddenColumns { LOCK.readLock().lock(); - Iterator it = new RegionsIterator(column, column); + Iterator it = new RegionsIterator(column, column, + hiddenColumns, cursor); while (it.hasNext()) { int[] region = it.next(); @@ -633,7 +646,7 @@ public class HiddenColumns StringBuffer visibleSeq = new StringBuffer(); Iterator blocks = new VisibleContigsIterator(start, - end + 1, false); + end + 1, hiddenColumns); while (blocks.hasNext()) { @@ -796,7 +809,7 @@ public class HiddenColumns int w = 0; Iterator blocks = new VisibleContigsIterator(start, end + 1, - false); + hiddenColumns); int copylength; int annotationLength; @@ -893,6 +906,7 @@ public class HiddenColumns { hideColumns(r[0], r[1]); } + cursor.resetCursor(hiddenColumns); } finally { LOCK.writeLock().unlock(); @@ -917,6 +931,7 @@ public class HiddenColumns } } hiddenColumns = null; + cursor.resetCursor(hiddenColumns); } finally { LOCK.writeLock().unlock(); @@ -934,7 +949,8 @@ public class HiddenColumns try { LOCK.writeLock().lock(); - Iterator it = new RegionsIterator(start, start); + Iterator it = new RegionsIterator(start, start, hiddenColumns, + cursor); while (it.hasNext()) { int[] region = it.next(); @@ -957,6 +973,7 @@ public class HiddenColumns { hiddenColumns = null; } + cursor.resetCursor(hiddenColumns); } finally { LOCK.writeLock().unlock(); @@ -1061,6 +1078,7 @@ public class HiddenColumns } hiddenColumns = newhidden; + cursor.resetCursor(hiddenColumns); } finally { LOCK.writeLock().unlock(); @@ -1164,6 +1182,7 @@ public class HiddenColumns lastSet = inserts.nextClearBit(firstSet); hideColumns(firstSet, lastSet - 1); } + cursor.resetCursor(hiddenColumns); } finally { LOCK.writeLock().unlock(); @@ -1250,7 +1269,6 @@ public class HiddenColumns { LOCK.readLock().unlock(); } - } /** @@ -1269,7 +1287,7 @@ public class HiddenColumns int[] reveal = null; Iterator it = new RegionsIterator(adjres - 2, - adjres + 2); + adjres + 2, hiddenColumns, cursor); while (it.hasNext()) { int[] region = it.next(); @@ -1291,7 +1309,14 @@ public class HiddenColumns */ public Iterator iterator() { - return new BoundedHiddenColsIterator(); + try + { + LOCK.readLock().lock(); + return new BoundedHiddenColsIterator(hiddenColumns); + } finally + { + LOCK.readLock().unlock(); + } } /** @@ -1305,7 +1330,14 @@ public class HiddenColumns */ public Iterator getBoundedIterator(int start, int end) { - return new BoundedHiddenColsIterator(start, end); + try + { + LOCK.readLock().lock(); + return new BoundedHiddenColsIterator(start, end, hiddenColumns); + } finally + { + LOCK.readLock().unlock(); + } } /** @@ -1319,7 +1351,14 @@ public class HiddenColumns */ public Iterator getBoundedStartIterator(int start, int end) { - return new BoundedStartRegionIterator(start, end, true); + try + { + LOCK.readLock().lock(); + return new BoundedStartRegionIterator(start, end, hiddenColumns); + } finally + { + LOCK.readLock().unlock(); + } } /** @@ -1333,7 +1372,14 @@ public class HiddenColumns */ public Iterator getVisibleColsIterator(int start, int end) { - return new VisibleColsIterator(start, end); + try + { + LOCK.readLock().lock(); + return new VisibleColsIterator(start, end, hiddenColumns); + } finally + { + LOCK.readLock().unlock(); + } } /** @@ -1347,8 +1393,14 @@ public class HiddenColumns */ public Iterator getVisContigsIterator(int start, int end) { - // return new VisibleBlocksIterator(start, end, true) - return new VisibleContigsIterator(start, end, true); + try + { + LOCK.readLock().lock(); + return new VisibleContigsIterator(start, end, hiddenColumns); + } finally + { + LOCK.readLock().unlock(); + } } /** @@ -1378,596 +1430,15 @@ public class HiddenColumns } else { - return new VisibleContigsIterator(start, end + 1, true); - } - } - - /** - * A local iterator which 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 RegionsIterator implements Iterator - { - // start position to iterate from - private int start; - - // end position to iterate to - private int end; - - // current index in hiddenColumns - private int currentPosition = 0; - - // current column in hiddenColumns - private int[] nextRegion = null; - - private int[] currentRegion = null; - - private int removedIndex = -1; - - // Constructor with bounds - RegionsIterator(int lowerBound, int upperBound) - { - start = lowerBound; - end = upperBound; - - if (hiddenColumns != null) - { - // iterate until a region overlaps with [start,end] - currentPosition = 0; - while ((currentPosition < hiddenColumns.size()) - && (hiddenColumns.get(currentPosition)[1] < start)) - { - currentPosition++; - } - if (currentPosition < hiddenColumns.size()) - { - nextRegion = hiddenColumns.get(currentPosition); - } - } - } - - @Override - public boolean hasNext() - { - return (hiddenColumns != null) && (nextRegion != null) - && (nextRegion[0] <= end); - } - - @Override - public int[] next() - { - currentRegion = nextRegion; - currentPosition++; - if (currentPosition < hiddenColumns.size()) - { - nextRegion = hiddenColumns.get(currentPosition); - } - else - { - nextRegion = null; - } - return currentRegion; - } - - @Override - public void remove() - { - if ((currentRegion != null) && (removedIndex != currentPosition)) - { - currentPosition--; - hiddenColumns.subList(currentPosition, currentPosition + 1).clear(); - removedIndex = currentPosition; - } - else - { - // already removed element last returned by next() - // or next() has not yet been called - throw new IllegalStateException(); - } - } - - } - - /** - * 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 - { - // 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. Intended to be used by - * callers OUTSIDE of HiddenColumns. - */ - private class BoundedHiddenColsIterator implements Iterator - { - // start position to iterate from - private int start; - - // end position to iterate to - private int end; - - // current index in hiddenColumns - private int currentPosition = 0; - - // current column in hiddenColumns - private int[] currentRegion; - - // local copy or reference to hiddenColumns - private List localHidden; - - /** - * Unbounded constructor - */ - BoundedHiddenColsIterator() - { - if (hiddenColumns != null) - { - int last = hiddenColumns.get(hiddenColumns.size() - 1)[1]; - init(0, last); - } - else - { - init(0, 0); - } - } - - /** - * Construct an iterator over hiddenColums bounded at - * [lowerBound,upperBound] - * - * @param lowerBound - * lower bound to iterate from - * @param upperBound - * upper bound to iterate to - */ - BoundedHiddenColsIterator(int lowerBound, int upperBound) - { - init(lowerBound, upperBound); - } - - /** - * Construct an iterator over hiddenColums bounded at - * [lowerBound,upperBound] - * - * @param lowerBound - * lower bound to iterate from - * @param upperBound - * upper bound to iterate to - */ - private void init(int lowerBound, int upperBound) - { - start = lowerBound; - end = upperBound; - try { 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 = hiddenColumns.get(i); - int[] cp = new int[2]; - System.arraycopy(rh, 0, cp, 0, rh.length); - localHidden.add(cp); - i++; - } - } - } - finally - { - LOCK.readLock().unlock(); - } - } - - @Override - public boolean hasNext() - { - return (localHidden != null) - && (currentPosition < localHidden.size()); - } - - @Override - public int[] next() - { - currentRegion = localHidden.get(currentPosition); - currentPosition++; - return currentRegion; - } - } - - /** - * An iterator which iterates over visible start positions of hidden column - * regions in a range. - */ - private class BoundedStartRegionIterator implements Iterator - { - // start position to iterate from - private int start; - - // end position to iterate to - private int end; - - // 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<>(); - } + return new VisibleContigsIterator(start, end + 1, hiddenColumns); } finally - { - if (useCopy) - { - LOCK.readLock().unlock(); - } - } - } - - @Override - public boolean hasNext() - { - return (currentPosition < positions.size()); - } - - /** - * Get next hidden region start position - * - * @return the start position in *visible* coordinates - */ - @Override - public Integer next() - { - int result = positions.get(currentPosition); - currentPosition++; - return result; - } - } - - /** - * Iterator over the visible *columns* (not regions) as determined by the set - * of hidden columns. Uses a local copy of hidden columns. - * - * @author kmourao - * - */ - private class VisibleColsIterator implements Iterator - { - private int last; - - private int current; - - private int next; - - private List localHidden = new ArrayList<>(); - - private int nexthiddenregion; - - VisibleColsIterator(int firstcol, int lastcol) { - last = lastcol; - current = firstcol; - next = firstcol; - nexthiddenregion = 0; - - LOCK.readLock().lock(); - - if (hiddenColumns != null) - { - int i = 0; - for (i = 0; i < hiddenColumns.size() - && (current <= hiddenColumns.get(i)[0]); ++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; - nexthiddenregion = i + 1; - } - } - - for (i = hiddenColumns.size() - 1; i >= 0 - && (last >= hiddenColumns.get(i)[1]); --i) - { - if (last >= hiddenColumns.get(i)[0] - && last <= hiddenColumns.get(i)[1]) - { - // last is hidden, move to left - last = hiddenColumns.get(i)[0] - 1; - } - } - - // make a local copy of the bit we need - i = nexthiddenregion; - 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++; - } - } - - 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) - && (nexthiddenregion < localHidden.size())) - { - // still some more hidden regions - if (next + 1 < localHidden.get(nexthiddenregion)[0]) - { - // next+1 is still before the next hidden region - next++; - } - else if ((next + 1 >= localHidden.get(nexthiddenregion)[0]) - && (next + 1 <= localHidden.get(nexthiddenregion)[1])) - { - // next + 1 is in the next hidden region - next = localHidden.get(nexthiddenregion)[1] + 1; - nexthiddenregion++; - } - } - else - { - // finished with hidden regions, just increment normally - next++; - } - return current; - } - - @Override - public void remove() - { - throw new UnsupportedOperationException(); - } - } - - /** - * An iterator which iterates over visible regions in a range. - */ - private class VisibleContigsIterator implements Iterator - { - private List 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(); - } + LOCK.readLock().unlock(); } } - - @Override - public boolean hasNext() - { - return (currentPosition < vcontigs.size()); - } - - @Override - public int[] next() - { - int[] result = vcontigs.get(currentPosition); - currentPosition++; - return result; - } } /** diff --git a/src/jalview/datamodel/HiddenColumnsCursor.java b/src/jalview/datamodel/HiddenColumnsCursor.java new file mode 100644 index 0000000..19c2969 --- /dev/null +++ b/src/jalview/datamodel/HiddenColumnsCursor.java @@ -0,0 +1,186 @@ +package jalview.datamodel; + +import java.util.List; + +public class HiddenColumnsCursor +{ + // absolute position of first hidden column + private int firstColumn; + + // absolute position of last hidden column + private int lastColumn; + + // index of last visited region + private int regionIndex; + + // number of hidden columns before last visited region + private int hiddenSoFar; + + // flag to indicate if current cursor settings are valid + private boolean isValid; + + private List hiddenColumns; + + protected HiddenColumnsCursor() + { + isValid = false; + } + + /** + * Set the cursor to a position + * + * @param first + * absolute position of first hidden column + * @param last + * absolute position of last hidden column + * @param index + * index of last visited region + * @param hiddenCount + * number of hidden columns before last visited region + */ + protected void resetCursor(// int first, int last, int index, int hiddenCount, + List hiddenCols) + { + isValid = true; + if ((hiddenCols != null) && (!hiddenCols.isEmpty())) + { + hiddenColumns = hiddenCols; + firstColumn = hiddenColumns.get(0)[0]; + lastColumn = hiddenColumns.get(hiddenColumns.size() - 1)[1]; + regionIndex = 0; + hiddenSoFar = 0; + } + } + + protected void updateCursor(int index, int hiddenCount) + { + regionIndex = index; + hiddenSoFar = hiddenCount; + } + + /** + * Get the index of the region that column is within (if column is hidden) or + * which is to the right of column (if column is visible). If no hidden + * columns are to the right, will return size of hiddenColumns. If hidden + * columns is empty returns -1. + * + * @param column + * absolute position of a column in the alignment + * @return region index + */ + protected int findRegionForColumn(int column) + { + if (hiddenColumns == null) + { + return -1; + } + + if ((hiddenColumns.get(regionIndex)[0] <= column) + && (hiddenColumns.get(regionIndex)[1] >= column)) + { + // we hit the jackpot + return regionIndex; + } + else if (column < firstColumn) + { + return 0; + } + else if (column > lastColumn) + { + return hiddenColumns.size(); + } + else if (column > hiddenColumns.get(regionIndex)[1]) + { + // iterate from where we are now, if we're lucky we'll be close by + // (but still better than iterating from 0) + while ((regionIndex < hiddenColumns.size()) + && (hiddenColumns.get(regionIndex)[0] <= column)) + { + int[] region = hiddenColumns.get(regionIndex); + hiddenSoFar += region[1] - region[0] + 1; + regionIndex++; + } + + } + else // (column < hiddenColumns.get(regionIndex)[0]) + { + int lastHidden = 0; + while ((regionIndex >= 0) + && (hiddenColumns.get(regionIndex)[0] > column)) + { + int[] region = hiddenColumns.get(regionIndex); + hiddenSoFar -= lastHidden; + lastHidden = region[1] - region[0] + 1; + regionIndex--; + } + hiddenSoFar -= lastHidden; + } + return regionIndex; + } + + protected int getHiddenOffset(int column) + { + if (hiddenColumns == null) + { + return -1; + } + + if (column < firstColumn) + { + return 0; + } + else if ((regionIndex < hiddenColumns.size()) + && (hiddenColumns.get(regionIndex)[0] <= column + + hiddenSoFar)) + { + + // iterate from where we are now, if we're lucky we'll be close by + // (but still better than iterating from 0) + while ((regionIndex < hiddenColumns.size()) + && (hiddenColumns.get(regionIndex)[0] <= column + + hiddenSoFar)) + { + int[] region = hiddenColumns.get(regionIndex); + hiddenSoFar += region[1] - region[0] + 1; + regionIndex++; + } + } + else if (regionIndex < hiddenColumns.size()) + { + int lastHidden = hiddenColumns.get(regionIndex)[1] + - hiddenColumns.get(regionIndex)[0] + 1; + + while ((regionIndex >= 0) + && (hiddenColumns.get(regionIndex)[0] <= column + hiddenSoFar + - lastHidden)) + { + int[] region = hiddenColumns.get(regionIndex); + hiddenSoFar -= lastHidden; + lastHidden = region[1] - region[0] + 1; + regionIndex--; + } + + } + + int result = hiddenSoFar; + if ((regionIndex >= 0) && (regionIndex < hiddenColumns.size()) + && (hiddenColumns.get(regionIndex)[0] <= column + hiddenSoFar) + && (hiddenColumns.get(regionIndex)[1] >= column + hiddenSoFar)) + { + int[] region = hiddenColumns.get(regionIndex); + result += region[1] - region[0] + 1; + } + + return result; + } + + /* public int findVisiblePositionFromAbsolute(int column) + { + + } + + public int findAbsolutePositionFromVisible(int column) + { + + }*/ +} diff --git a/src/jalview/datamodel/RegionsIterator.java b/src/jalview/datamodel/RegionsIterator.java new file mode 100644 index 0000000..d1251f8 --- /dev/null +++ b/src/jalview/datamodel/RegionsIterator.java @@ -0,0 +1,98 @@ +package jalview.datamodel; + +import java.util.Iterator; +import java.util.List; + +/** + * A local iterator which 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). + */ +public class RegionsIterator implements Iterator +{ + // start position to iterate from + private int start; + + // end position to iterate to + private int end; + + // current index in hiddenColumns + private int currentPosition = 0; + + // current column in hiddenColumns + private int[] nextRegion = null; + + private int[] currentRegion = null; + + private int removedIndex = -1; + + private final List hiddenColumns; + + // Constructor with bounds + RegionsIterator(int lowerBound, int upperBound, List hiddenCols, + HiddenColumnsCursor cursor) + { + start = lowerBound; + end = upperBound; + hiddenColumns = hiddenCols; + + if (hiddenColumns != null) + { + currentPosition = cursor.findRegionForColumn(start); + + // iterate until a region overlaps with [start,end] + /* currentPosition = 0; + while ((currentPosition < hiddenColumns.size()) + && (hiddenColumns.get(currentPosition)[1] < start)) + { + currentPosition++; + }*/ + if (currentPosition < hiddenColumns.size()) + { + nextRegion = hiddenColumns.get(currentPosition); + } + } + } + + @Override + public boolean hasNext() + { + return (hiddenColumns != null) && (nextRegion != null) + && (nextRegion[0] <= end); + } + + @Override + public int[] next() + { + currentRegion = nextRegion; + currentPosition++; + if (currentPosition < hiddenColumns.size()) + { + nextRegion = hiddenColumns.get(currentPosition); + } + else + { + nextRegion = null; + } + return currentRegion; + } + + @Override + public void remove() + { + if ((currentRegion != null) && (removedIndex != currentPosition)) + { + currentPosition--; + hiddenColumns.subList(currentPosition, currentPosition + 1).clear(); + removedIndex = currentPosition; + } + else + { + // already removed element last returned by next() + // or next() has not yet been called + throw new IllegalStateException(); + } + } + +} diff --git a/src/jalview/datamodel/ReverseRegionsIterator.java b/src/jalview/datamodel/ReverseRegionsIterator.java new file mode 100644 index 0000000..b511ab5 --- /dev/null +++ b/src/jalview/datamodel/ReverseRegionsIterator.java @@ -0,0 +1,75 @@ +package jalview.datamodel; + +import java.util.Iterator; +import java.util.List; + +/** + * 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). + */ +public class ReverseRegionsIterator implements Iterator +{ + // 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; + + private final List hiddenColumns; + + // Constructor with bounds + ReverseRegionsIterator(int lowerBound, int upperBound, + List hiddenCols) + { + hiddenColumns = hiddenCols; + 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; + } + +} diff --git a/src/jalview/datamodel/VisibleColsIterator.java b/src/jalview/datamodel/VisibleColsIterator.java new file mode 100644 index 0000000..2fa27ed --- /dev/null +++ b/src/jalview/datamodel/VisibleColsIterator.java @@ -0,0 +1,116 @@ +package jalview.datamodel; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; + +/** + * Iterator over the visible *columns* (not regions) as determined by the set of + * hidden columns. Uses a local copy of hidden columns. + * + * @author kmourao + * + */ +public class VisibleColsIterator implements Iterator +{ + private int last; + + private int current; + + private int next; + + private List localHidden = new ArrayList<>(); + + private int nexthiddenregion; + + VisibleColsIterator(int firstcol, int lastcol, List hiddenColumns) + { + last = lastcol; + current = firstcol; + next = firstcol; + nexthiddenregion = 0; + + if (hiddenColumns != null) + { + int i = 0; + for (i = 0; i < hiddenColumns.size() + && (current <= hiddenColumns.get(i)[0]); ++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; + nexthiddenregion = i + 1; + } + } + + for (i = hiddenColumns.size() - 1; i >= 0 + && (last >= hiddenColumns.get(i)[1]); --i) + { + if (last >= hiddenColumns.get(i)[0] + && last <= hiddenColumns.get(i)[1]) + { + // last is hidden, move to left + last = hiddenColumns.get(i)[0] - 1; + } + } + + // make a local copy of the bit we need + i = nexthiddenregion; + 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++; + } + } + } + + @Override + public boolean hasNext() + { + return next <= last; + } + + @Override + public Integer next() + { + if (next > last) + { + throw new NoSuchElementException(); + } + current = next; + if ((localHidden != null) && (nexthiddenregion < localHidden.size())) + { + // still some more hidden regions + if (next + 1 < localHidden.get(nexthiddenregion)[0]) + { + // next+1 is still before the next hidden region + next++; + } + else if ((next + 1 >= localHidden.get(nexthiddenregion)[0]) + && (next + 1 <= localHidden.get(nexthiddenregion)[1])) + { + // next + 1 is in the next hidden region + next = localHidden.get(nexthiddenregion)[1] + 1; + nexthiddenregion++; + } + } + else + { + // finished with hidden regions, just increment normally + next++; + } + return current; + } + + @Override + public void remove() + { + throw new UnsupportedOperationException(); + } +} diff --git a/src/jalview/datamodel/VisibleContigsIterator.java b/src/jalview/datamodel/VisibleContigsIterator.java new file mode 100644 index 0000000..d7594f9 --- /dev/null +++ b/src/jalview/datamodel/VisibleContigsIterator.java @@ -0,0 +1,76 @@ +package jalview.datamodel; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * An iterator which iterates over visible regions in a range. + */ +public class VisibleContigsIterator implements Iterator +{ + private List vcontigs = new ArrayList<>(); + + private int currentPosition = 0; + + VisibleContigsIterator(int start, int end, + List hiddenColumns) + { + 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); + } + } + + @Override + public boolean hasNext() + { + return (currentPosition < vcontigs.size()); + } + + @Override + public int[] next() + { + int[] result = vcontigs.get(currentPosition); + currentPosition++; + return result; + } +} + -- 1.7.10.2