- * the column to start from
- * @return the position of the column in the visible alignment
- */
- public int subtractVisibleColumns(int visibleDistance, int startColumn)
- {
- try
- {
- LOCK.readLock().lock();
- int distance = visibleDistance;
-
- // in case startColumn is in a hidden region, move it to the left
- int start = adjustForHiddenColumns(findColumnPosition(startColumn));
-
- Iterator<int[]> it = new ReverseRegionsIterator(0, start,
- hiddenColumns);
-
- while (it.hasNext() && (distance > 0))
- {
- int[] region = it.next();
-
- if (start > region[1])
- {
- // subtract the gap to right of region from distance
- if (start - region[1] <= distance)
- {
- distance -= start - region[1];
- start = region[0] - 1;
- }
- else
- {
- start = start - distance;
- distance = 0;
- }
- }
- }
-
- return start - distance;
-
- } finally
- {
- LOCK.readLock().unlock();
- }
- }
-
- /**
- * This method returns the rightmost limit of a region of an alignment with
- * hidden columns. In otherwords, the next hidden column.
- *
- * @param alPos
- * the absolute (visible) alignmentPosition to find the next hidden
- * column for
- */
- public int getHiddenBoundaryRight(int alPos)
- {
- try
- {
- LOCK.readLock().lock();
- if (hiddenColumns != null)
- {
- int index = cursor.findRegionForColumn(alPos);
- if (index < hiddenColumns.size())
- {
- int[] region = hiddenColumns.get(index);
- if (alPos < region[0])
- {
- return region[0];
- }
- else if ((alPos <= region[1])
- && (index + 1 < hiddenColumns.size()))
- {
- // alPos is within a hidden region, return the next one
- // if there is one
- region = hiddenColumns.get(index + 1);
- return region[0];
- }
- }
- }
- return alPos;
- } finally
- {
- 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);
- }
-
- /**
- * delete any columns in alignmentAnnotation that are hidden (including
- * sequence associated annotation).
- *
- * @param start
- * remove any annotation to the right of this column
- * @param end
- * remove any annotation to the left of this column
- * @param alignmentAnnotation
- * the annotation to operate on
- */
- public void makeVisibleAnnotation(int start, int end,
- AlignmentAnnotation alignmentAnnotation)
- {
- try
- {
- LOCK.readLock().lock();
-
- int startFrom = start;
- int endAt = end;
-
- if (alignmentAnnotation.annotations != null)
- {
- if (hiddenColumns != null && hiddenColumns.size() > 0)
- {
- removeHiddenAnnotation(startFrom, endAt, alignmentAnnotation);
- }
- else
- {
- alignmentAnnotation.restrict(startFrom, endAt);
- }
- }
- } finally
- {
- LOCK.readLock().unlock();
- }
- }
-
- private void removeHiddenAnnotation(int start, int end,
- AlignmentAnnotation alignmentAnnotation)
- {
- // mangle the alignmentAnnotation annotation array
- ArrayList<Annotation[]> annels = new ArrayList<>();
- Annotation[] els = null;
-
- int w = 0;
-
- Iterator<int[]> blocks = new VisibleContigsIterator(start, end + 1,
- hiddenColumns);
-
- int copylength;
- int annotationLength;
- while (blocks.hasNext())
- {
- int[] block = blocks.next();
- annotationLength = block[1] - block[0] + 1;
-
- if (blocks.hasNext())
- {
- // copy just the visible segment of the annotation row
- copylength = annotationLength;
- }
- else
- {
- if (annotationLength + block[0] <= alignmentAnnotation.annotations.length)
- {
- // copy just the visible segment of the annotation row
- copylength = annotationLength;
- }
- else
- {
- // copy to the end of the annotation row
- copylength = alignmentAnnotation.annotations.length - block[0];
- }
- }
-
- els = new Annotation[annotationLength];
- annels.add(els);
- System.arraycopy(alignmentAnnotation.annotations, block[0], els, 0,
- copylength);
- w += annotationLength;
- }
-
- if (w != 0)
- {
- alignmentAnnotation.annotations = new Annotation[w];
-
- w = 0;
- for (Annotation[] chnk : annels)
- {
- System.arraycopy(chnk, 0, alignmentAnnotation.annotations, w,
- chnk.length);
- w += chnk.length;
- }
- }
- }
-
- /**
- *
- * @return true if there are columns hidden
- */
- public boolean hasHiddenColumns()
- {
- try
- {
- LOCK.readLock().lock();
- return hiddenColumns != null && hiddenColumns.size() > 0;
- } finally
- {
- LOCK.readLock().unlock();
- }
- }
-
- /**
- *
- * @return true if there are more than one set of columns hidden