X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;ds=sidebyside;f=src%2Fjalview%2Fdatamodel%2FHiddenColumnsCursor.java;h=d6b6c10d0059c870af672ea7474d508fca1f7f61;hb=62fb9a3042bdc6daaad8218d47b12cb912907354;hp=0fef622df7916d49fbb1492a3a30a6ae27c7bdaf;hpb=fcd1956579a9ecf87f659096bcf6e17975c10193;p=jalview.git diff --git a/src/jalview/datamodel/HiddenColumnsCursor.java b/src/jalview/datamodel/HiddenColumnsCursor.java index 0fef622..d6b6c10 100644 --- a/src/jalview/datamodel/HiddenColumnsCursor.java +++ b/src/jalview/datamodel/HiddenColumnsCursor.java @@ -20,41 +20,34 @@ */ package jalview.datamodel; +import java.util.ArrayList; import java.util.List; -import java.util.concurrent.atomic.AtomicReference; public class HiddenColumnsCursor { // absolute position of first hidden column private int firstColumn; - private List hiddenColumns; + private List hiddenColumns = new ArrayList<>(); - // AtomicReference to hold the current region index and hidden column count - // Could be done with synchronisation but benchmarking shows this way is 2x - // faster - private final AtomicReference cursorPos = new AtomicReference<>( - new HiddenCursorPosition(0, 0)); + private HiddenCursorPosition cursorPos = new HiddenCursorPosition(0, 0); protected HiddenColumnsCursor() { } - /** - * Reset the cursor with a new hidden columns collection. Calls to resetCursor - * should be made from within a writeLock in the HiddenColumns class - since - * changes to the hiddenColumns collection require a writeLock the lock should - * already exist. - * - * @param hiddenCols - * new hidden columns collection - */ - protected void resetCursor(List hiddenCols) + protected HiddenColumnsCursor(List hiddenCols) { resetCursor(hiddenCols, 0, 0); } + protected HiddenColumnsCursor(List hiddenCols, int index, + int hiddencount) + { + resetCursor(hiddenCols, index, hiddencount); + } + /** * Reset the cursor with a new hidden columns collection, where we know in * advance the index and hidden columns count of a particular location. @@ -66,53 +59,15 @@ public class HiddenColumnsCursor * @param hiddencount * hidden columns count to reset to */ - protected void resetCursor(List hiddenCols, int index, + private void resetCursor(List hiddenCols, int index, int hiddencount) { hiddenColumns = hiddenCols; - if ((hiddenCols != null) && (!hiddenCols.isEmpty())) + if (!hiddenCols.isEmpty()) { firstColumn = hiddenColumns.get(0)[0]; - HiddenCursorPosition oldpos = cursorPos.get(); - HiddenCursorPosition newpos = new HiddenCursorPosition(index, - hiddencount); - cursorPos.compareAndSet(oldpos, newpos); - } - } - - /** - * Delete the region the cursor is currently at. Avoids having to reset the - * cursor just because we deleted a region. - * - * Calls to updateForDeletedRegion should be made from within a writeLock in - * the HiddenColumns class - since changes to the hiddenColumns collection - * require a writeLock the lock should already exist. - * - * @param hiddenCols - */ - protected void updateForDeletedRegion(List hiddenCols) - { - - if ((hiddenCols != null) && (!hiddenCols.isEmpty())) - { - // if there is a region to the right of the current region, - // nothing changes; otherwise - // we deleted the last region (index=hiddenCols.size()-1) - // or the index was at the end of the alignment (index=hiddenCols.size()) - HiddenCursorPosition oldpos = cursorPos.get(); - - int index = oldpos.getRegionIndex(); - if (index >= hiddenColumns.size() - 1) - { - // deleted last region, index is now end of alignment - index = hiddenCols.size(); - - HiddenCursorPosition newpos = new HiddenCursorPosition(index, - oldpos.getHiddenSoFar()); - cursorPos.compareAndSet(oldpos, newpos); - } + cursorPos = new HiddenCursorPosition(index, hiddencount); } - hiddenColumns = hiddenCols; } /** @@ -124,131 +79,134 @@ public class HiddenColumnsCursor * If hidden columns is empty returns null. * * @param column - * absolute position of a column in the alignment + * index of column in visible or absolute coordinates + * @param useVisible + * true if column is in visible coordinates, false if absolute * @return cursor pointing to hidden region containing the column (if hidden) * or to the right of the column (if visible) */ - protected HiddenCursorPosition findRegionForColumn(int column) + protected HiddenCursorPosition findRegionForColumn(int column, + boolean useVisible) { - if (hiddenColumns == null) + if (hiddenColumns.isEmpty()) { return null; } - HiddenCursorPosition oldpos = cursorPos.get(); - int index = oldpos.getRegionIndex(); - int hiddenCount = oldpos.getHiddenSoFar(); + // used to add in hiddenColumns offset when working with visible columns + int offset = (useVisible ? 1 : 0); - if (index == hiddenColumns.size()) - { - // went past the end of hiddenColumns collection last time - index--; - int[] region = hiddenColumns.get(index); - hiddenCount -= region[1] - region[0] + 1; - } + HiddenCursorPosition pos = cursorPos; + int index = pos.getRegionIndex(); + int hiddenCount = pos.getHiddenSoFar(); - // this if statement excludes case where column is in current region - // - no changes needed if (column < firstColumn) { - index = 0; - hiddenCount = 0; + pos = new HiddenCursorPosition(0, 0); } + // column is after current region - else if (column > hiddenColumns.get(index)[1]) + else if ((index < hiddenColumns.size()) + && (hiddenColumns.get(index)[0] <= column + + offset * hiddenCount)) { // iterate from where we are now, if we're lucky we'll be close by // (but still better than iterating from 0) // stop when we find the region *before* column // i.e. the next region starts after column or if not, ends after column - while ((index < hiddenColumns.size()) - && (column > hiddenColumns.get(index)[1])) - { - int[] region = hiddenColumns.get(index); - hiddenCount += region[1] - region[0] + 1; - index++; - } - } - // column is before current region - else if (column < hiddenColumns.get(index)[0]) - { - // column is before or in the previous region - while ((index > 0) && (hiddenColumns.get(index - 1)[1] >= column)) - { - index--; - int[] region = hiddenColumns.get(index); - hiddenCount -= region[1] - region[0] + 1; - } + pos = searchForward(pos, column, useVisible); } - if (index != oldpos.getRegionIndex() - || hiddenCount != oldpos.getHiddenSoFar()) + // column is before current region + else { - HiddenCursorPosition newpos = new HiddenCursorPosition(index, - hiddenCount); - cursorPos.compareAndSet(oldpos, newpos); - return newpos; + pos = searchBackward(pos, column, useVisible); } - return oldpos; + cursorPos = pos; + return pos; } /** - * Get the cursor pointing to the hidden region just after a visible column + * Search forwards through the hidden columns collection to find the hidden + * region immediately before a column * + * @param pos + * current position * @param column - * index of column in *visible* alignment (therefore by definition - * column is visible) - * @return cursor pointing to hidden region to the right of the column + * column to locate + * @param useVisible + * whether using visible or absolute coordinates + * @return position of region before column */ - protected HiddenCursorPosition findRegionForVisColumn(int column) + private HiddenCursorPosition searchForward(HiddenCursorPosition pos, + int column, boolean useVisible) { - if (hiddenColumns == null) - { - return null; - } - - HiddenCursorPosition oldpos = cursorPos.get(); - int index = oldpos.getRegionIndex(); - int hiddenCount = oldpos.getHiddenSoFar(); - - if (column < firstColumn) - { - index = 0; - hiddenCount = 0; - } - else if ((index < hiddenColumns.size()) - && (hiddenColumns.get(index)[0] <= column + hiddenCount)) + HiddenCursorPosition p = pos; + if (useVisible) { - // iterate from where we are now, if we're lucky we'll be close by - // (but still better than iterating from 0) - while ((index < hiddenColumns.size()) - && (hiddenColumns.get(index)[0] <= column + hiddenCount)) + while ((p.getRegionIndex() < hiddenColumns.size()) + && hiddenColumns.get(p.getRegionIndex())[0] <= column + + p.getHiddenSoFar()) { - int[] region = hiddenColumns.get(index); - hiddenCount += region[1] - region[0] + 1; - index++; + p = stepForward(p); } } else { - while ((index > 0) - && (hiddenColumns.get(index - 1)[1] >= column + hiddenCount)) + while ((p.getRegionIndex() < hiddenColumns.size()) + && hiddenColumns.get(p.getRegionIndex())[1] < column) { - index--; - int[] region = hiddenColumns.get(index); - hiddenCount -= region[1] - region[0] + 1; + p = stepForward(p); } - } + return p; + } + + /** + * Move to the next (rightwards) hidden region after a given cursor position + * + * @param p + * current position of cursor + * @return new position of cursor at next region + */ + private HiddenCursorPosition stepForward(HiddenCursorPosition p) + { + int[] region = hiddenColumns.get(p.getRegionIndex()); + + // increment the index, and add this region's hidden columns to the hidden + // column count + return new HiddenCursorPosition(p.getRegionIndex() + 1, + p.getHiddenSoFar() + region[1] - region[0] + 1); + } - if (index != oldpos.getRegionIndex() - || hiddenCount != oldpos.getHiddenSoFar()) + /** + * Search backwards through the hidden columns collection to find the hidden + * region immediately before (left of) a given column + * + * @param pos + * current position + * @param column + * column to locate + * @param useVisible + * whether using visible or absolute coordinates + * @return position of region immediately to left of column + */ + private HiddenCursorPosition searchBackward(HiddenCursorPosition p, + int column, boolean useVisible) + { + int i = p.getRegionIndex(); + int h = p.getHiddenSoFar(); + + // used to add in hiddenColumns offset when working with visible columns + int offset = (useVisible ? 1 : 0); + + while ((i > 0) && (hiddenColumns.get(i - 1)[1] >= column + offset * h)) { - HiddenCursorPosition newpos = new HiddenCursorPosition(index, - hiddenCount); - cursorPos.compareAndSet(oldpos, newpos); - return newpos; + i--; + int[] region = hiddenColumns.get(i); + h -= region[1] - region[0] + 1; } - return oldpos; + return new HiddenCursorPosition(i, h); } + }