X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fdatamodel%2FHiddenColumns.java;h=2d43f025f7bc6746b216f37d9aee2feb13330bb1;hb=c6018dc0dc12720e13b75850a5303279ac7094b7;hp=b903826c83071cb44cfb966b2dc037fc8ef041ae;hpb=e48cf231d302b758416cc9a3b38939853cf6b3bf;p=jalview.git diff --git a/src/jalview/datamodel/HiddenColumns.java b/src/jalview/datamodel/HiddenColumns.java index b903826..2d43f02 100644 --- a/src/jalview/datamodel/HiddenColumns.java +++ b/src/jalview/datamodel/HiddenColumns.java @@ -75,7 +75,8 @@ public class HiddenColumns private HiddenColumnsCursor cursor = new HiddenColumnsCursor(); /* - * cache of the number of hidden columns + * cache of the number of hidden columns: must be kept up to date by methods + * which add or remove hidden columns */ private int numColumns = 0; @@ -140,7 +141,7 @@ public class HiddenColumns numColumns += region[1] - region[0] + 1; } } - cursor.resetCursor(hiddenColumns); + cursor = new HiddenColumnsCursor(hiddenColumns); } } finally { @@ -168,7 +169,7 @@ public class HiddenColumns if (!hiddenColumns.isEmpty()) { // set up cursor reset values - HiddenCursorPosition cursorPos = cursor.findRegionForColumn(start); + HiddenCursorPosition cursorPos = cursor.findRegionForColumn(start, false); regionindex = cursorPos.getRegionIndex(); if (regionindex > 0) @@ -181,13 +182,13 @@ public class HiddenColumns } } - /* - * new range follows everything else; check first to avoid looping over whole hiddenColumns collection - */ + // new range follows everything else; check first to avoid looping over + // whole hiddenColumns collection if (hiddenColumns.isEmpty() || start > hiddenColumns.get(hiddenColumns.size() - 1)[1]) { hiddenColumns.add(new int[] { start, end }); + numColumns += end - start + 1; } else { @@ -208,11 +209,8 @@ public class HiddenColumns // reset the cursor to just before our insertion point: this saves // a lot of reprocessing in large alignments - cursor.resetCursor(hiddenColumns, previndex, prevHiddenCount); - - // reset the number of columns so they will be recounted - numColumns = 0; - + cursor = new HiddenColumnsCursor(hiddenColumns, previndex, + prevHiddenCount); } finally { LOCK.writeLock().unlock(); @@ -242,6 +240,7 @@ public class HiddenColumns * insert discontiguous preceding range */ hiddenColumns.add(i, new int[] { start, end }); + numColumns += end - start + 1; added = true; } else if (end <= region[1]) @@ -250,7 +249,10 @@ public class HiddenColumns * new range overlaps existing, or is contiguous preceding it - adjust * start column */ + int oldstart = region[0]; region[0] = Math.min(region[0], start); + numColumns += oldstart - region[0]; // new columns are between old and + // adjusted starts added = true; } else if (start <= region[1] + 1) @@ -259,32 +261,55 @@ public class HiddenColumns * 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); + insertRangeAtOverlap(i, start, end, region); + added = true; + } + return added; + } - /* - * also update or remove any subsequent ranges - * that are overlapped - */ - while (i < hiddenColumns.size() - 1) + /** + * Insert a range whose start position overlaps an existing region and/or is + * contiguous to the right of the region + * + * @param i + * index to insert at + * @param start + * start of range to insert + * @param end + * end of range to insert + * @param region + * the overlapped/continued region + */ + private void insertRangeAtOverlap(int i, int start, int end, int[] region) + { + int oldstart = region[0]; + int oldend = region[1]; + region[0] = Math.min(region[0], start); + region[1] = Math.max(region[1], end); + + numColumns += oldstart - region[0]; + + /* + * also update or remove any subsequent ranges + * that are overlapped + */ + int endi = i; + while (endi < hiddenColumns.size() - 1) + { + int[] nextRegion = hiddenColumns.get(endi + 1); + if (nextRegion[0] > end + 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); - - // in theory hiddenColumns.subList(i + 1, i + 2).clear() is faster than - // hiddenColumns.remove(i+1) but benchmarking results a bit ambivalent - hiddenColumns.remove(i + 1); + /* + * gap to next hidden range - no more to update + */ + break; } - added = true; + numColumns -= nextRegion[1] - nextRegion[0] + 1; + region[1] = Math.max(nextRegion[1], end); + endi++; } - return added; + numColumns += region[1] - oldend; + hiddenColumns.subList(i + 1, endi + 1).clear(); } /** @@ -301,8 +326,8 @@ public class HiddenColumns { hideColumns(r[0], r[1]); } - cursor.resetCursor(hiddenColumns); - numColumns = 0; + cursor = new HiddenColumnsCursor(hiddenColumns); + } finally { LOCK.writeLock().unlock(); @@ -326,7 +351,7 @@ public class HiddenColumns } } hiddenColumns.clear(); - cursor.resetCursor(hiddenColumns); + cursor = new HiddenColumnsCursor(hiddenColumns); numColumns = 0; } finally @@ -352,7 +377,7 @@ public class HiddenColumns if (!hiddenColumns.isEmpty()) { - int regionIndex = cursor.findRegionForColumn(start) + int regionIndex = cursor.findRegionForColumn(start, false) .getRegionIndex(); if (regionIndex != -1 && regionIndex != hiddenColumns.size()) @@ -368,17 +393,7 @@ public class HiddenColumns } int colsToRemove = region[1] - region[0] + 1; hiddenColumns.remove(regionIndex); - - if (hiddenColumns.isEmpty()) - { - hiddenColumns.clear(); - numColumns = 0; - } - else - { - numColumns -= colsToRemove; - } - cursor.updateForDeletedRegion(hiddenColumns); + numColumns -= colsToRemove; } } } @@ -434,28 +449,7 @@ public class HiddenColumns */ public int getSize() { - try - { - LOCK.readLock().lock(); - - if (numColumns == 0) - { - // numColumns is out of date, so recalculate - int size = 0; - - for (int[] range : hiddenColumns) - { - size += range[1] - range[0] + 1; - } - - numColumns = size; - } - - return numColumns; - } finally - { - LOCK.readLock().unlock(); - } + return numColumns; } /** @@ -475,6 +469,10 @@ public class HiddenColumns } } + /** + * Answers true if obj is an instance of HiddenColumns, and holds the same + * array of start-end column ranges as this, else answers false + */ @Override public boolean equals(Object obj) { @@ -530,7 +528,8 @@ public class HiddenColumns if (!hiddenColumns.isEmpty()) { - result += cursor.findRegionForVisColumn(column).getHiddenSoFar(); + result += cursor.findRegionForColumn(column, true) + .getHiddenSoFar(); } return result; @@ -560,7 +559,7 @@ public class HiddenColumns if (!hiddenColumns.isEmpty()) { HiddenCursorPosition cursorPos = cursor - .findRegionForColumn(hiddenColumn); + .findRegionForColumn(hiddenColumn, false); int index = cursorPos.getRegionIndex(); int hiddenBeforeCol = cursorPos.getHiddenSoFar(); @@ -642,7 +641,8 @@ public class HiddenColumns LOCK.readLock().lock(); if (!hiddenColumns.isEmpty()) { - int index = cursor.findRegionForColumn(alPos).getRegionIndex(); + int index = cursor.findRegionForColumn(alPos, false) + .getRegionIndex(); if (left && index > 0) { @@ -686,15 +686,19 @@ public class HiddenColumns { LOCK.readLock().lock(); - int regionindex = cursor.findRegionForColumn(column).getRegionIndex(); - if (regionindex > -1 && regionindex < hiddenColumns.size()) + if (!hiddenColumns.isEmpty()) { - int[] region = hiddenColumns.get(regionindex); - // already know that column <= region[1] as cursor returns containing - // region or region to right - if (column >= region[0]) + int regionindex = cursor.findRegionForColumn(column, false) + .getRegionIndex(); + if (regionindex > -1 && regionindex < hiddenColumns.size()) { - return false; + int[] region = hiddenColumns.get(regionindex); + // already know that column <= region[1] as cursor returns containing + // region or region to right + if (column >= region[0]) + { + return false; + } } } return true; @@ -706,75 +710,6 @@ public class HiddenColumns } /** - * Locate the first position visible for this sequence. If seq isn't visible - * then return the position of the left side of the hidden boundary region. - * - * @param seq - * sequence to find position for - * @return visible start position - */ - public int locateVisibleStartOfSequence(SequenceI seq) - { - try - { - LOCK.readLock().lock(); - int start = 0; - - if (hiddenColumns.isEmpty()) - { - return seq.findIndex(seq.getStart()) - 1; - } - - // Simply walk along the sequence whilst watching for hidden column - // boundaries - Iterator regions = hiddenColumns.iterator(); - int hideStart = seq.getLength(); - int hideEnd = -1; - int visPrev = 0; - int visNext = 0; - boolean foundStart = false; - - // step through the non-gapped positions of the sequence - for (int i = seq.getStart(); i <= seq.getEnd() && (!foundStart); i++) - { - // get alignment position of this residue in the sequence - int p = seq.findIndex(i) - 1; - - // update hidden region start/end - while (hideEnd < p && regions.hasNext()) - { - int[] region = regions.next(); - visPrev = visNext; - visNext += region[0] - visPrev; - hideStart = region[0]; - hideEnd = region[1]; - } - if (hideEnd < p) - { - hideStart = seq.getLength(); - } - // update visible boundary for sequence - if (p < hideStart) - { - start = p; - foundStart = true; - } - } - - if (foundStart) - { - return absoluteToVisibleColumn(start); - } - // otherwise, sequence was completely hidden - return visPrev; - } finally - { - LOCK.readLock().unlock(); - } - } - - - /** * * @return true if there are columns hidden */ @@ -841,18 +776,41 @@ public class HiddenColumns */ public void hideColumns(BitSet inserts) { + hideColumns(inserts, 0, inserts.length() - 1); + } + + /** + * Hide columns corresponding to the marked bits, within the range + * [start,end]. Entries in tohide which are outside [start,end] are ignored. + * + * @param tohide + * columns mapped to bits starting from zero + * @param start + * start of range to hide columns within + * @param end + * end of range to hide columns within + */ + private void hideColumns(BitSet tohide, int start, int end) + { try { LOCK.writeLock().lock(); - for (int firstSet = inserts - .nextSetBit(0), lastSet = 0; firstSet >= 0; firstSet = inserts + for (int firstSet = tohide + .nextSetBit(start), lastSet = start; firstSet >= start + && lastSet <= end; firstSet = tohide .nextSetBit(lastSet)) { - lastSet = inserts.nextClearBit(firstSet); - hideColumns(firstSet, lastSet - 1); + lastSet = tohide.nextClearBit(firstSet); + if (lastSet <= end) + { + hideColumns(firstSet, lastSet - 1); + } + else if (firstSet <= end) + { + hideColumns(firstSet, end); + } } - cursor.resetCursor(hiddenColumns); - numColumns = 0; + cursor = new HiddenColumnsCursor(hiddenColumns); } finally { LOCK.writeLock().unlock(); @@ -862,6 +820,7 @@ public class HiddenColumns /** * Hide columns corresponding to the marked bits, within the range * [start,end]. Entries in tohide which are outside [start,end] are ignored. + * NB Existing entries in [start,end] are cleared. * * @param tohide * columns mapped to bits starting from zero @@ -870,19 +829,10 @@ public class HiddenColumns * @param end * end of range to hide columns within */ - public void hideColumns(BitSet tohide, int start, int end) + public void clearAndHideColumns(BitSet tohide, int start, int end) { clearHiddenColumnsInRange(start, end); - - // make sure only bits between start and end are set - if (!tohide.isEmpty()) - { - tohide.clear(0, start); - tohide.clear(Math.min(end + 1, tohide.length() + 1), - tohide.length() + 1); - } - - hideColumns(tohide); + hideColumns(tohide, start, end); } /** @@ -901,9 +851,8 @@ public class HiddenColumns if (!hiddenColumns.isEmpty()) { - HiddenCursorPosition pos = cursor.findRegionForColumn(start); + HiddenCursorPosition pos = cursor.findRegionForColumn(start, false); int index = pos.getRegionIndex(); - int startindex = index; // first index in hiddenColumns to remove if (index != -1 && index != hiddenColumns.size()) { @@ -913,30 +862,35 @@ public class HiddenColumns if (region[0] < start && region[1] >= start) { // region contains start, truncate so that it ends just before start + numColumns -= region[1] - start + 1; region[1] = start - 1; - startindex++; + index++; } - } - - pos = cursor.findRegionForColumn(end); - index = pos.getRegionIndex(); - int endindex = index - 1; // last index in hiddenColumns to remove - if (index != -1 && index != hiddenColumns.size()) - { - // regionIndex is the region which either contains end - // or lies to the right of end - int[] region = hiddenColumns.get(index); - if (region[0] <= end && region[1] > end) + int endi = index; + while (endi < hiddenColumns.size()) { - // region contains end, truncate so that it starts just after end - region[0] = end + 1; + region = hiddenColumns.get(endi); + + if (region[1] > end) + { + if (region[0] <= end) + { + // region contains end, truncate so it starts just after end + numColumns -= end - region[0] + 1; + region[0] = end + 1; + } + break; + } + + numColumns -= region[1] - region[0] + 1; + endi++; } + hiddenColumns.subList(index, endi).clear(); + } - hiddenColumns.subList(startindex, endindex + 1).clear(); - cursor.resetCursor(hiddenColumns); - numColumns = 0; + cursor = new HiddenColumnsCursor(hiddenColumns); } } finally { @@ -1028,7 +982,7 @@ public class HiddenColumns if (!hiddenColumns.isEmpty()) { // look for a region ending just before adjres - int regionindex = cursor.findRegionForColumn(adjres - 1) + int regionindex = cursor.findRegionForColumn(adjres - 1, false) .getRegionIndex(); if (regionindex < hiddenColumns.size() && hiddenColumns.get(regionindex)[1] == adjres - 1) @@ -1058,7 +1012,7 @@ public class HiddenColumns try { LOCK.readLock().lock(); - return new HiddenColsIterator(hiddenColumns); + return new RangeIterator(hiddenColumns); } finally { LOCK.readLock().unlock(); @@ -1079,7 +1033,7 @@ public class HiddenColumns try { LOCK.readLock().lock(); - return new HiddenColsIterator(start, end, hiddenColumns); + return new RangeIterator(start, end, hiddenColumns); } finally { LOCK.readLock().unlock(); @@ -1095,7 +1049,7 @@ public class HiddenColumns * @param end * position to end at (inclusive, visible column position) */ - public Iterator getBoundedStartIterator(int start, int end) + public Iterator getStartRegionIterator(int start, int end) { try { @@ -1110,9 +1064,9 @@ public class HiddenColumns // region we'll get the cursor pointing to the region before, which is // what we want HiddenCursorPosition pos = cursor - .findRegionForColumn(absoluteStart - 1); + .findRegionForColumn(absoluteStart - 1, false); - return new BoundedStartRegionIterator(pos, start, end, + return new StartRegionIterator(pos, start, end, hiddenColumns); } finally { @@ -1134,7 +1088,8 @@ public class HiddenColumns try { LOCK.readLock().lock(); - return new VisibleColsIterator(start, end, hiddenColumns); + return new RangeElementsIterator( + new VisibleContigsIterator(start, end + 1, hiddenColumns)); } finally { LOCK.readLock().unlock();