X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fdatamodel%2FHiddenColumnsCursor.java;h=a875f874fa33154ea1baa31712f99547fba21684;hb=62086b8b7a0b9436c445a94bfab5eff253adb6d5;hp=2d579b0263ebb4cd276f12bec8eaed536d0ef30e;hpb=44be050a48b372e5e29c74a0b838c73269501e0e;p=jalview.git diff --git a/src/jalview/datamodel/HiddenColumnsCursor.java b/src/jalview/datamodel/HiddenColumnsCursor.java index 2d579b0..a875f87 100644 --- a/src/jalview/datamodel/HiddenColumnsCursor.java +++ b/src/jalview/datamodel/HiddenColumnsCursor.java @@ -20,20 +20,22 @@ */ 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; - // index of last visited region - private int regionIndex; + private List hiddenColumns = new ArrayList<>(); - // number of hidden columns before last visited region - private int hiddenSoFar; - - private List hiddenColumns; + // 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)); protected HiddenColumnsCursor() { @@ -41,88 +43,102 @@ public class HiddenColumnsCursor } /** - * Set the cursor to a position + * 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) { - synchronized (this) + resetCursor(hiddenCols, 0, 0); + } + + /** + * Reset the cursor with a new hidden columns collection, where we know in + * advance the index and hidden columns count of a particular location. + * + * @param hiddenCols + * new hidden columns collection + * @param index + * cursor index to reset to + * @param hiddencount + * hidden columns count to reset to + */ + protected void resetCursor(List hiddenCols, int index, + int hiddencount) + { + hiddenColumns = hiddenCols; + if (!hiddenCols.isEmpty()) { - hiddenColumns = hiddenCols; - if ((hiddenCols != null) && (!hiddenCols.isEmpty())) - { - firstColumn = hiddenColumns.get(0)[0]; - regionIndex = 0; - hiddenSoFar = 0; - } + 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 (!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()) - if (regionIndex >= hiddenColumns.size() - 1) + HiddenCursorPosition oldpos = cursorPos.get(); + + int index = oldpos.getRegionIndex(); + if (index >= hiddenColumns.size() - 1) { // deleted last region, index is now end of alignment - regionIndex = hiddenCols.size(); + index = hiddenCols.size(); + + HiddenCursorPosition newpos = new HiddenCursorPosition(index, + oldpos.getHiddenSoFar()); + cursorPos.compareAndSet(oldpos, newpos); } } - hiddenColumns = hiddenCols; } - protected void updateCursor(int index, int hiddenCount) - { - synchronized (this) - { - regionIndex = index; - hiddenSoFar = hiddenCount; - } - } - - protected synchronized int getIndex() - { - return regionIndex; - } - - protected synchronized int getHiddenSoFar() - { - return hiddenSoFar; - } - - /** - * 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. + * Get the cursor pointing to the hidden 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, returns a cursor pointing + * to an imaginary hidden region beyond the end of the hidden columns + * collection (this ensures the count of previous hidden columns is correct). + * If hidden columns is empty returns null. * * @param column * absolute position of a column in the alignment - * @return region index + * @return cursor pointing to hidden region containing the column (if hidden) + * or to the right of the column (if visible) */ - protected int findRegionForColumn(int column) + protected HiddenCursorPosition findRegionForColumn(int column) { - if (hiddenColumns == null) + if (hiddenColumns.isEmpty()) { - return -1; + return null; } - int index = regionIndex; - int hiddenCount = hiddenSoFar; + HiddenCursorPosition oldpos = cursorPos.get(); + int index = oldpos.getRegionIndex(); + int hiddenCount = oldpos.getHiddenSoFar(); if (index == hiddenColumns.size()) { @@ -132,21 +148,15 @@ public class HiddenColumnsCursor hiddenCount -= region[1] - region[0] + 1; } - if ((hiddenColumns.get(index)[0] <= column) - && hiddenColumns.get(index)[1] >= column) - { - // column is in the current region - // we hit the jackpot - // don't need to move index - } - else if (column < firstColumn) + // this if statement excludes case where column is in current region + // - no changes needed + if (column < firstColumn) { index = 0; hiddenCount = 0; } // column is after current region - else if (column > hiddenColumns.get(index)[1]) // includes if column > - // lastColumn + else if (column > hiddenColumns.get(index)[1]) { // iterate from where we are now, if we're lucky we'll be close by // (but still better than iterating from 0) @@ -160,42 +170,47 @@ public class HiddenColumnsCursor index++; } } - // column is before current region else if (column < hiddenColumns.get(index)[0]) { // column is before or in the previous region - if ((index > 0) && (hiddenColumns.get(index - 1)[1] >= column)) + while ((index > 0) && (hiddenColumns.get(index - 1)[1] >= column)) { - while ((index > 0) && (hiddenColumns.get(index)[1] > column)) - { - index--; - int[] region = hiddenColumns.get(index); - hiddenCount -= region[1] - region[0] + 1; - } + index--; + int[] region = hiddenColumns.get(index); + hiddenCount -= region[1] - region[0] + 1; } } - updateCursor(index, hiddenCount); - return index; + + if (index != oldpos.getRegionIndex() + || hiddenCount != oldpos.getHiddenSoFar()) + { + HiddenCursorPosition newpos = new HiddenCursorPosition(index, + hiddenCount); + cursorPos.compareAndSet(oldpos, newpos); + return newpos; + } + return oldpos; } /** - * Get the number of hidden columns in regions before column i.e. excludes - * hidden columns in the region column is in, if any + * Get the cursor pointing to the hidden region just after a visible column * * @param column - * index of column in visible alignment - * @return + * index of column in *visible* alignment (therefore by definition + * column is visible) + * @return cursor pointing to hidden region to the right of the column */ - protected int getHiddenOffset(int column) + protected HiddenCursorPosition findRegionForVisColumn(int column) { - if (hiddenColumns == null) + if (hiddenColumns.isEmpty()) { - return -1; + return null; } - int index = getIndex(); - int hiddenCount = getHiddenSoFar(); + HiddenCursorPosition oldpos = cursorPos.get(); + int index = oldpos.getRegionIndex(); + int hiddenCount = oldpos.getHiddenSoFar(); if (column < firstColumn) { @@ -226,7 +241,15 @@ public class HiddenColumnsCursor } } - updateCursor(index, hiddenCount); - return hiddenCount; + + if (index != oldpos.getRegionIndex() + || hiddenCount != oldpos.getHiddenSoFar()) + { + HiddenCursorPosition newpos = new HiddenCursorPosition(index, + hiddenCount); + cursorPos.compareAndSet(oldpos, newpos); + return newpos; + } + return oldpos; } }