+ }
+
+ /**
+ * Adjust hidden column boundaries based on a series of column additions or
+ * deletions in visible regions.
+ *
+ * @param shiftrecord
+ * @return
+ */
+ private ShiftList compensateForEdits(ShiftList shiftrecord)
+ {
+ if (shiftrecord != null)
+ {
+ final List<int[]> shifts = shiftrecord.getShifts();
+ if (shifts != null && shifts.size() > 0)
+ {
+ int shifted = 0;
+ for (int i = 0, j = shifts.size(); i < j; i++)
+ {
+ int[] sh = shifts.get(i);
+ compensateForDelEdits(shifted + sh[0], sh[1]);
+ shifted -= sh[1];
+ }
+ }
+ return shiftrecord.getInverse();
+ }
+ return null;
+ }
+
+ /**
+ * Returns a hashCode built from hidden column ranges
+ */
+ @Override
+ public int hashCode()
+ {
+ try
+ {
+ LOCK.readLock().lock();
+ int hashCode = 1;
+ if (hiddenColumns != null)
+ {
+ for (int[] hidden : hiddenColumns)
+ {
+ hashCode = 31 * hashCode + hidden[0];
+ hashCode = 31 * hashCode + hidden[1];
+ }
+ }
+ return hashCode;
+ } finally
+ {
+ LOCK.readLock().unlock();
+ }
+ }
+
+ /**
+ * Hide columns corresponding to the marked bits
+ *
+ * @param inserts
+ * - columns map to bits starting from zero
+ */
+ public void hideMarkedBits(BitSet inserts)
+ {
+ try
+ {
+ LOCK.writeLock().lock();
+ for (int firstSet = inserts
+ .nextSetBit(0), lastSet = 0; firstSet >= 0; firstSet = inserts
+ .nextSetBit(lastSet))
+ {
+ lastSet = inserts.nextClearBit(firstSet);
+ hideColumns(firstSet, lastSet - 1);
+ }
+ } finally
+ {
+ LOCK.writeLock().unlock();
+ }
+ }
+
+ /**
+ *
+ * @param inserts
+ * BitSet where hidden columns will be marked
+ */
+ public void markHiddenRegions(BitSet inserts)
+ {
+ try
+ {
+ LOCK.readLock().lock();
+ if (hiddenColumns == null)
+ {
+ return;
+ }
+ for (int[] range : hiddenColumns)
+ {
+ inserts.set(range[0], range[1] + 1);
+ }
+ } finally
+ {
+ LOCK.readLock().unlock();
+ }
+ }
+
+ /**
+ * Calculate the visible start and end index of an alignment.
+ *
+ * @param width
+ * full alignment width
+ * @return integer array where: int[0] = startIndex, and int[1] = endIndex
+ */
+ public int[] getVisibleStartAndEndIndex(int width)
+ {
+ try
+ {
+ LOCK.readLock().lock();
+ int[] alignmentStartEnd = new int[] { 0, width - 1 };
+ int startPos = alignmentStartEnd[0];
+ int endPos = alignmentStartEnd[1];
+
+ int[] lowestRange = new int[] { -1, -1 };
+ int[] higestRange = new int[] { -1, -1 };
+
+ if (hiddenColumns == null)
+ {
+ return new int[] { startPos, endPos };
+ }
+
+ for (int[] hiddenCol : hiddenColumns)
+ {
+ lowestRange = (hiddenCol[0] <= startPos) ? hiddenCol : lowestRange;
+ higestRange = (hiddenCol[1] >= endPos) ? hiddenCol : higestRange;
+ }
+
+ if (lowestRange[0] == -1 && lowestRange[1] == -1)
+ {
+ startPos = alignmentStartEnd[0];
+ }
+ else
+ {
+ startPos = lowestRange[1] + 1;
+ }
+
+ if (higestRange[0] == -1 && higestRange[1] == -1)
+ {
+ endPos = alignmentStartEnd[1];
+ }
+ else
+ {
+ endPos = higestRange[0] - 1;
+ }
+ return new int[] { startPos, endPos };
+ } finally
+ {
+ LOCK.readLock().unlock();
+ }
+
+ }
+
+ /**
+ * Finds the hidden region (if any) which starts or ends at res
+ *
+ * @param res
+ * visible residue position, unadjusted for hidden columns
+ * @return region as [start,end] or null if no matching region is found
+ */
+ public int[] getRegionWithEdgeAtRes(int res)
+ {
+ try
+ {
+ LOCK.readLock().lock();
+ int adjres = adjustForHiddenColumns(res);
+
+ int[] reveal = null;
+ if (hiddenColumns != null)
+ {
+ for (int[] region : hiddenColumns)
+ {
+ if (adjres + 1 == region[0] || adjres - 1 == region[1])
+ {
+ reveal = region;
+ break;
+ }
+ }
+ }
+ return reveal;
+ } finally
+ {
+ LOCK.readLock().unlock();
+ }