- {
- LOCK.readLock().unlock();
- }
- }
-
- /**
- * This method returns the leftmost limit of a region of an alignment with
- * hidden columns. In otherwords, the previous hidden column.
- *
- * @param alPos
- * the absolute (visible) alignmentPosition to find the previous
- * hidden column for
- */
- public int getHiddenBoundaryLeft(int alPos)
- {
- try
- {
- LOCK.readLock().lock();
-
- if (hiddenColumns != null)
- {
- int index = cursor.findRegionForColumn(alPos);
-
- if (index > 0)
- {
- int[] region = hiddenColumns.get(index - 1);
- return region[1];
- }
- }
- return alPos;
- } finally
- {
- LOCK.readLock().unlock();
- }
- }
-
- /**
- * Adds the specified column range to the hidden columns collection
- *
- * @param start
- * start of range to add (absolute position in alignment)
- * @param end
- * end of range to add (absolute position in alignment)
- */
- public void hideColumns(int start, int end)
- {
- boolean wasAlreadyLocked = false;
- try
- {
- // check if the write lock was already locked by this thread,
- // as this method can be called internally in loops within HiddenColumns
- if (!LOCK.isWriteLockedByCurrentThread())
- {
- LOCK.writeLock().lock();
- }
- else
- {
- wasAlreadyLocked = true;
- }
-
- if (hiddenColumns == null)
- {
- hiddenColumns = new ArrayList<>();
- }
-
- /*
- * 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 });
- }
- else
- {
- /*
- * traverse existing hidden ranges and insert / amend / append as
- * appropriate
- */
- boolean added = false;
- for (int i = 0; !added && i < hiddenColumns.size(); i++)
- {
- added = insertRangeAtRegion(i, start, end);
- } // for
- }
- if (!wasAlreadyLocked)
- {
- cursor.resetCursor(hiddenColumns);
- }
- } finally
- {
- if (!wasAlreadyLocked)
- {
- LOCK.writeLock().unlock();
- }
- }
- }
-
- /**
- * Insert [start, range] at the region at index i in hiddenColumns, if
- * feasible
- *
- * @param i
- * index to insert at
- * @param start
- * start of range to insert
- * @param end
- * end of range to insert
- * @return true if range was successfully inserted
- */
- private boolean insertRangeAtRegion(int i, int start, int end)
- {
- boolean added = false;
-
- int[] region = hiddenColumns.get(i);
- if (end < region[0] - 1)
- {
- /*
- * insert discontiguous preceding range
- */
- hiddenColumns.add(i, new int[] { start, end });
- added = true;
- }
- else if (end <= region[1])
- {
- /*
- * new range overlaps existing, or is contiguous preceding it - adjust
- * start column
- */
- region[0] = Math.min(region[0], start);
- added = true;
- }
- else if (start <= region[1] + 1)
- {
- /*
- * 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);
-
- /*
- * also update or remove any subsequent ranges
- * that are overlapped
- */
- while (i < hiddenColumns.size() - 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);
- hiddenColumns.subList(i + 1, i + 2).clear();
- }
- added = true;
- }
- return added;
- }
-
- /**
- * Answers if a column in the alignment is visible
- *
- * @param column
- * absolute position of column in the alignment
- * @return true if column is visible
- */
- public boolean isVisible(int column)
- {
- try
- {
- LOCK.readLock().lock();
-
- Iterator<int[]> it = new RegionsIterator(column, column,
- hiddenColumns, cursor);
- while (it.hasNext())
- {
- int[] region = it.next();
- if (column >= region[0] && column <= region[1])
- {
- return false;
- }
- }
-
- return true;
- } finally
- {
- LOCK.readLock().unlock();
- }
- }
-
- /**
- * Get the visible sections of a set of sequences
- *
- * @param start
- * sequence position to start from
- * @param end
- * sequence position to end at
- * @param seqs
- * an array of sequences
- * @return an array of strings encoding the visible parts of each sequence
- */
- public String[] getVisibleSequenceStrings(int start, int end,
- SequenceI[] seqs)
- {
- try
- {
- LOCK.readLock().lock();
- int iSize = seqs.length;
- String[] selections = new String[iSize];
- if (hiddenColumns != null && hiddenColumns.size() > 0)
- {
- for (int i = 0; i < iSize; i++)
- {
- StringBuffer visibleSeq = new StringBuffer();
-
- Iterator<int[]> blocks = new VisibleContigsIterator(start,
- end + 1, hiddenColumns);
-
- while (blocks.hasNext())
- {
- int[] block = blocks.next();
- if (blocks.hasNext())
- {
- visibleSeq
- .append(seqs[i].getSequence(block[0], block[1] + 1));
- }
- else
- {
- visibleSeq
- .append(seqs[i].getSequence(block[0], block[1]));
- }
- }
-
- selections[i] = visibleSeq.toString();
- }
- }
- else
- {
- for (int i = 0; i < iSize; i++)
- {
- selections[i] = seqs[i].getSequenceAsString(start, end);
- }
- }
-
- return selections;
- } finally
- {
- LOCK.readLock().unlock();
- }
- }
-
- /**
- * 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 == null || hiddenColumns.size() == 0)
- {
- return seq.findIndex(seq.getStart()) - 1;
- }
-
- // Simply walk along the sequence whilst watching for hidden column
- // boundaries
- Iterator<int[]> 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 findColumnPosition(start);
- }
- // otherwise, sequence was completely hidden
- return visPrev;
- } finally
- {
- LOCK.readLock().unlock();
- }
- }
-
- /**
- * delete any columns in alignmentAnnotation that are hidden (including
- * sequence associated annotation).
- *
- * @param alignmentAnnotation
- */
- public void makeVisibleAnnotation(AlignmentAnnotation alignmentAnnotation)
- {
- makeVisibleAnnotation(0, alignmentAnnotation.annotations.length,
- alignmentAnnotation);