2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.datamodel;
23 import java.util.ArrayList;
24 import java.util.BitSet;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.concurrent.locks.ReentrantReadWriteLock;
29 public class HiddenColumns
31 private static final int HASH_MULTIPLIER = 31;
33 private static final ReentrantReadWriteLock LOCK = new ReentrantReadWriteLock();
35 private HiddenColumnsCursor cursor = new HiddenColumnsCursor();
37 private int numColumns = 0;
40 * list of hidden column [start, end] ranges; the list is maintained in
41 * ascending start column order
43 private ArrayList<int[]> hiddenColumns;
48 public HiddenColumns()
57 public HiddenColumns(HiddenColumns copy)
61 LOCK.writeLock().lock();
64 if (copy.hiddenColumns != null)
66 hiddenColumns = new ArrayList<>();
67 Iterator<int[]> it = copy.iterator();
70 hiddenColumns.add(it.next());
72 cursor.resetCursor(hiddenColumns);
77 LOCK.writeLock().unlock();
82 * Copy constructor within bounds and with offset. Copies hidden column
83 * regions fully contained between start and end, and offsets positions by
87 * HiddenColumns instance to copy from
89 * lower bound to copy from
91 * upper bound to copy to
93 * offset to subtract from each region boundary position
96 public HiddenColumns(HiddenColumns copy, int start, int end, int offset)
100 LOCK.writeLock().lock();
103 hiddenColumns = new ArrayList<>();
105 Iterator<int[]> it = copy.getBoundedIterator(start, end);
108 int[] region = it.next();
109 // still need to check boundaries because iterator returns
110 // all overlapping regions and we need contained regions
111 if (region[0] >= start && region[1] <= end)
115 { region[0] - offset, region[1] - offset });
116 numColumns += region[1] - region[0] + 1;
119 cursor.resetCursor(hiddenColumns);
123 LOCK.writeLock().unlock();
128 * Output regions data as a string. String is in the format:
129 * reg0[0]<between>reg0[1]<delimiter>reg1[0]<between>reg1[1] ... regn[1]
132 * string to delimit regions
133 * @param betweenstring
134 * to put between start and end region values
135 * @return regions formatted according to delimiter and between strings
137 public String regionsToString(String delimiter, String between)
141 LOCK.readLock().lock();
142 StringBuilder regionBuilder = new StringBuilder();
143 if (hiddenColumns != null)
145 Iterator<int[]> it = hiddenColumns.iterator();
148 int[] range = it.next();
149 regionBuilder.append(delimiter).append(range[0]).append(between)
153 regionBuilder.deleteCharAt(0);
157 return regionBuilder.toString();
160 LOCK.readLock().unlock();
165 * Find the number of hidden columns
167 * @return number of hidden columns
173 LOCK.readLock().lock();
175 if (numColumns == 0 && hiddenColumns != null)
177 // numColumns is out of date, so recalculate
179 if (hiddenColumns != null)
181 Iterator<int[]> it = hiddenColumns.iterator();
184 int[] range = it.next();
185 size += range[1] - range[0] + 1;
194 LOCK.readLock().unlock();
199 * Get the number of distinct hidden regions
201 * @return number of regions
203 public int getNumberOfRegions()
207 LOCK.readLock().lock();
209 if (hasHiddenColumns())
211 num = hiddenColumns.size();
216 LOCK.readLock().unlock();
221 public boolean equals(Object obj)
225 LOCK.readLock().lock();
227 if (!(obj instanceof HiddenColumns))
231 HiddenColumns that = (HiddenColumns) obj;
234 * check hidden columns are either both null, or match
236 if (this.hiddenColumns == null)
238 return (that.hiddenColumns == null);
240 if (that.hiddenColumns == null
241 || that.hiddenColumns.size() != this.hiddenColumns.size())
246 Iterator<int[]> it = hiddenColumns.iterator();
247 Iterator<int[]> thatit = that.iterator();
250 int[] thisRange = it.next();
251 int[] thatRange = thatit.next();
252 if (thisRange[0] != thatRange[0] || thisRange[1] != thatRange[1])
260 LOCK.readLock().unlock();
265 * Return absolute column index for a visible column index
268 * int column index in alignment view (count from zero)
269 * @return alignment column index for column
271 public int adjustForHiddenColumns(int column)
275 LOCK.readLock().lock();
278 if (hiddenColumns != null)
280 result += cursor.getHiddenOffset(column).getHiddenSoFar();
286 LOCK.readLock().unlock();
291 * Use this method to find out where a column will appear in the visible
292 * alignment when hidden columns exist. If the column is not visible, then the
293 * left-most visible column will always be returned.
295 * @param hiddenColumn
296 * the column index in the full alignment including hidden columns
297 * @return the position of the column in the visible alignment
299 public int findColumnPosition(int hiddenColumn)
303 LOCK.readLock().lock();
304 int result = hiddenColumn;
306 if (hiddenColumns != null)
308 HiddenCursorPosition cursorPos = cursor
309 .findRegionForColumn(hiddenColumn);
310 int index = cursorPos.getRegionIndex();
311 int hiddenBeforeCol = cursorPos.getHiddenSoFar();
313 // just subtract hidden cols count - this works fine if column is
315 result = hiddenColumn - hiddenBeforeCol;
317 // now check in case column is hidden - it will be in the returned
319 if (index < hiddenColumns.size())
321 int[] region = hiddenColumns.get(index);
322 if (hiddenColumn >= region[0] && hiddenColumn <= region[1])
324 // actually col is hidden, return region[0]-1
325 // unless region[0]==0 in which case return 0
332 result = region[0] - 1 - hiddenBeforeCol;
338 return result; // return the shifted position after removing hidden
342 LOCK.readLock().unlock();
347 * Find the visible column which is a given visible number of columns to the
348 * left of another visible column. i.e. for a startColumn x, the column which
349 * is distance 1 away will be column x-1.
351 * @param visibleDistance
352 * the number of visible columns to offset by
354 * the column to start from
355 * @return the position of the column in the visible alignment
357 public int subtractVisibleColumns(int visibleDistance, int startColumn)
361 LOCK.readLock().lock();
362 int distance = visibleDistance;
364 // in case startColumn is in a hidden region, move it to the left
365 int start = adjustForHiddenColumns(findColumnPosition(startColumn));
367 Iterator<int[]> it = new ReverseRegionsIterator(0, start,
370 while (it.hasNext() && (distance > 0))
372 int[] region = it.next();
374 if (start > region[1])
376 // subtract the gap to right of region from distance
377 if (start - region[1] <= distance)
379 distance -= start - region[1];
380 start = region[0] - 1;
384 start = start - distance;
390 return start - distance;
394 LOCK.readLock().unlock();
399 * This method returns the rightmost limit of a region of an alignment with
400 * hidden columns. In otherwords, the next hidden column.
403 * the absolute (visible) alignmentPosition to find the next hidden
406 public int getHiddenBoundaryRight(int alPos)
410 LOCK.readLock().lock();
411 if (hiddenColumns != null)
413 int index = cursor.findRegionForColumn(alPos).getRegionIndex();
414 if (index < hiddenColumns.size())
416 int[] region = hiddenColumns.get(index);
417 if (alPos < region[0])
421 else if ((alPos <= region[1])
422 && (index + 1 < hiddenColumns.size()))
424 // alPos is within a hidden region, return the next one
426 region = hiddenColumns.get(index + 1);
434 LOCK.readLock().unlock();
439 * This method returns the leftmost limit of a region of an alignment with
440 * hidden columns. In otherwords, the previous hidden column.
443 * the absolute (visible) alignmentPosition to find the previous
446 public int getHiddenBoundaryLeft(int alPos)
450 LOCK.readLock().lock();
452 if (hiddenColumns != null)
454 int index = cursor.findRegionForColumn(alPos).getRegionIndex();
458 int[] region = hiddenColumns.get(index - 1);
465 LOCK.readLock().unlock();
470 * Adds the specified column range to the hidden columns collection
473 * start of range to add (absolute position in alignment)
475 * end of range to add (absolute position in alignment)
477 public void hideColumns(int start, int end)
479 boolean wasAlreadyLocked = false;
482 // check if the write lock was already locked by this thread,
483 // as this method can be called internally in loops within HiddenColumns
484 if (!LOCK.isWriteLockedByCurrentThread())
486 LOCK.writeLock().lock();
490 wasAlreadyLocked = true;
493 if (hiddenColumns == null)
495 hiddenColumns = new ArrayList<>();
499 * new range follows everything else; check first to avoid looping over whole hiddenColumns collection
501 if (hiddenColumns.isEmpty()
502 || start > hiddenColumns.get(hiddenColumns.size() - 1)[1])
504 hiddenColumns.add(new int[] { start, end });
509 * traverse existing hidden ranges and insert / amend / append as
512 boolean added = false;
513 for (int i = 0; !added && i < hiddenColumns.size(); i++)
515 added = insertRangeAtRegion(i, start, end);
518 if (!wasAlreadyLocked)
520 cursor.resetCursor(hiddenColumns);
522 // reset the number of columns so they will be recounted
527 if (!wasAlreadyLocked)
529 LOCK.writeLock().unlock();
535 * Insert [start, range] at the region at index i in hiddenColumns, if
541 * start of range to insert
543 * end of range to insert
544 * @return true if range was successfully inserted
546 private boolean insertRangeAtRegion(int i, int start, int end)
548 boolean added = false;
550 int[] region = hiddenColumns.get(i);
551 if (end < region[0] - 1)
554 * insert discontiguous preceding range
556 hiddenColumns.add(i, new int[] { start, end });
559 else if (end <= region[1])
562 * new range overlaps existing, or is contiguous preceding it - adjust
565 region[0] = Math.min(region[0], start);
568 else if (start <= region[1] + 1)
571 * new range overlaps existing, or is contiguous following it - adjust
572 * start and end columns
574 region[0] = Math.min(region[0], start);
575 region[1] = Math.max(region[1], end);
578 * also update or remove any subsequent ranges
579 * that are overlapped
581 while (i < hiddenColumns.size() - 1)
583 int[] nextRegion = hiddenColumns.get(i + 1);
584 if (nextRegion[0] > end + 1)
587 * gap to next hidden range - no more to update
591 region[1] = Math.max(nextRegion[1], end);
592 hiddenColumns.subList(i + 1, i + 2).clear();
600 * Answers if a column in the alignment is visible
603 * absolute position of column in the alignment
604 * @return true if column is visible
606 public boolean isVisible(int column)
610 LOCK.readLock().lock();
612 int regionindex = cursor.findRegionForColumn(column).getRegionIndex();
613 if (regionindex > -1 && regionindex < hiddenColumns.size())
615 int[] region = hiddenColumns.get(regionindex);
616 if (column >= region[0] && column <= region[1])
625 LOCK.readLock().unlock();
630 * Get the visible sections of a set of sequences
633 * sequence position to start from
635 * sequence position to end at
637 * an array of sequences
638 * @return an array of strings encoding the visible parts of each sequence
640 public String[] getVisibleSequenceStrings(int start, int end,
645 LOCK.readLock().lock();
646 int iSize = seqs.length;
647 String[] selections = new String[iSize];
648 if (hiddenColumns != null && hiddenColumns.size() > 0)
650 for (int i = 0; i < iSize; i++)
652 StringBuffer visibleSeq = new StringBuffer();
654 Iterator<int[]> blocks = new VisibleContigsIterator(start,
655 end + 1, hiddenColumns);
657 while (blocks.hasNext())
659 int[] block = blocks.next();
660 if (blocks.hasNext())
663 .append(seqs[i].getSequence(block[0], block[1] + 1));
668 .append(seqs[i].getSequence(block[0], block[1]));
672 selections[i] = visibleSeq.toString();
677 for (int i = 0; i < iSize; i++)
679 selections[i] = seqs[i].getSequenceAsString(start, end);
686 LOCK.readLock().unlock();
691 * Locate the first position visible for this sequence. If seq isn't visible
692 * then return the position of the left side of the hidden boundary region.
695 * sequence to find position for
696 * @return visible start position
698 public int locateVisibleStartOfSequence(SequenceI seq)
702 LOCK.readLock().lock();
705 if (hiddenColumns == null || hiddenColumns.size() == 0)
707 return seq.findIndex(seq.getStart()) - 1;
710 // Simply walk along the sequence whilst watching for hidden column
712 Iterator<int[]> regions = hiddenColumns.iterator();
713 int hideStart = seq.getLength();
717 boolean foundStart = false;
719 // step through the non-gapped positions of the sequence
720 for (int i = seq.getStart(); i <= seq.getEnd() && (!foundStart); i++)
722 // get alignment position of this residue in the sequence
723 int p = seq.findIndex(i) - 1;
725 // update hidden region start/end
726 while (hideEnd < p && regions.hasNext())
728 int[] region = regions.next();
730 visNext += region[0] - visPrev;
731 hideStart = region[0];
736 hideStart = seq.getLength();
738 // update visible boundary for sequence
748 return findColumnPosition(start);
750 // otherwise, sequence was completely hidden
754 LOCK.readLock().unlock();
759 * delete any columns in alignmentAnnotation that are hidden (including
760 * sequence associated annotation).
762 * @param alignmentAnnotation
764 public void makeVisibleAnnotation(AlignmentAnnotation alignmentAnnotation)
766 makeVisibleAnnotation(0, alignmentAnnotation.annotations.length,
767 alignmentAnnotation);
771 * delete any columns in alignmentAnnotation that are hidden (including
772 * sequence associated annotation).
775 * remove any annotation to the right of this column
777 * remove any annotation to the left of this column
778 * @param alignmentAnnotation
779 * the annotation to operate on
781 public void makeVisibleAnnotation(int start, int end,
782 AlignmentAnnotation alignmentAnnotation)
786 LOCK.readLock().lock();
788 int startFrom = start;
791 if (alignmentAnnotation.annotations != null)
793 if (hiddenColumns != null && hiddenColumns.size() > 0)
795 removeHiddenAnnotation(startFrom, endAt, alignmentAnnotation);
799 alignmentAnnotation.restrict(startFrom, endAt);
804 LOCK.readLock().unlock();
808 private void removeHiddenAnnotation(int start, int end,
809 AlignmentAnnotation alignmentAnnotation)
811 // mangle the alignmentAnnotation annotation array
812 ArrayList<Annotation[]> annels = new ArrayList<>();
813 Annotation[] els = null;
817 Iterator<int[]> blocks = new VisibleContigsIterator(start, end + 1,
821 int annotationLength;
822 while (blocks.hasNext())
824 int[] block = blocks.next();
825 annotationLength = block[1] - block[0] + 1;
827 if (blocks.hasNext())
829 // copy just the visible segment of the annotation row
830 copylength = annotationLength;
834 if (annotationLength + block[0] <= alignmentAnnotation.annotations.length)
836 // copy just the visible segment of the annotation row
837 copylength = annotationLength;
841 // copy to the end of the annotation row
842 copylength = alignmentAnnotation.annotations.length - block[0];
846 els = new Annotation[annotationLength];
848 System.arraycopy(alignmentAnnotation.annotations, block[0], els, 0,
850 w += annotationLength;
855 alignmentAnnotation.annotations = new Annotation[w];
858 for (Annotation[] chnk : annels)
860 System.arraycopy(chnk, 0, alignmentAnnotation.annotations, w,
869 * @return true if there are columns hidden
871 public boolean hasHiddenColumns()
875 LOCK.readLock().lock();
876 return hiddenColumns != null && hiddenColumns.size() > 0;
879 LOCK.readLock().unlock();
885 * @return true if there are more than one set of columns hidden
887 public boolean hasManyHiddenColumns()
891 LOCK.readLock().lock();
892 return hiddenColumns != null && hiddenColumns.size() > 1;
895 LOCK.readLock().unlock();
900 * mark the columns corresponding to gap characters as hidden in the column
905 public void hideInsertionsFor(SequenceI sr)
909 LOCK.writeLock().lock();
910 List<int[]> inserts = sr.getInsertions();
911 for (int[] r : inserts)
913 hideColumns(r[0], r[1]);
915 cursor.resetCursor(hiddenColumns);
919 LOCK.writeLock().unlock();
924 * Unhides, and adds to the selection list, all hidden columns
926 public void revealAllHiddenColumns(ColumnSelection sel)
930 LOCK.writeLock().lock();
931 if (hiddenColumns != null)
933 Iterator<int[]> it = hiddenColumns.iterator();
936 int[] region = it.next();
937 for (int j = region[0]; j < region[1] + 1; j++)
942 hiddenColumns = null;
943 cursor.resetCursor(hiddenColumns);
948 LOCK.writeLock().unlock();
953 * Reveals, and marks as selected, the hidden column range with the given
958 public void revealHiddenColumns(int start, ColumnSelection sel)
962 LOCK.writeLock().lock();
964 if (hiddenColumns != null)
966 int regionIndex = cursor.findRegionForColumn(start)
969 if (regionIndex != -1 && regionIndex != hiddenColumns.size())
971 // regionIndex is the region which either contains start
972 // or lies to the right of start
973 int[] region = hiddenColumns.get(regionIndex);
974 if (start == region[0])
976 for (int j = region[0]; j < region[1] + 1; j++)
980 int colsToRemove = region[1] - region[0] + 1;
981 hiddenColumns.remove(regionIndex);
983 if (hiddenColumns.isEmpty())
985 hiddenColumns = null;
990 numColumns -= colsToRemove;
992 cursor.updateForDeletedRegion(hiddenColumns);
999 LOCK.writeLock().unlock();
1004 * Add gaps into the sequences aligned to profileseq under the given
1009 * - alignment to have gaps inserted into it
1011 * - alignment view where sequence corresponding to profileseq is
1013 * @return new HiddenColumns for new alignment view, with insertions into
1014 * profileseq marked as hidden.
1016 public static HiddenColumns propagateInsertions(SequenceI profileseq,
1017 AlignmentI al, AlignmentView input)
1021 char gc = al.getGapCharacter();
1022 Object[] alandhidden = input.getAlignmentAndHiddenColumns(gc);
1023 HiddenColumns nview = (HiddenColumns) alandhidden[1];
1024 SequenceI origseq = ((SequenceI[]) alandhidden[0])[profsqpos];
1025 nview.propagateInsertions(profileseq, al, origseq);
1032 * - sequence in al which corresponds to origseq
1034 * - alignment which is to have gaps inserted into it
1036 * - sequence corresponding to profileseq which defines gap map for
1039 private void propagateInsertions(SequenceI profileseq, AlignmentI al,
1044 LOCK.writeLock().lock();
1046 char gc = al.getGapCharacter();
1048 // take the set of hidden columns, and the set of gaps in origseq,
1049 // and remove all the hidden gaps from hiddenColumns
1051 // first get the gaps as a Bitset
1052 BitSet gaps = origseq.gapBitset();
1054 // now calculate hidden ^ not(gap)
1055 BitSet hidden = new BitSet();
1056 markHiddenRegions(hidden);
1057 hidden.andNot(gaps);
1058 hiddenColumns = null;
1059 this.hideMarkedBits(hidden);
1061 // for each sequence in the alignment, except the profile sequence,
1062 // insert gaps corresponding to each hidden region
1063 // but where each hidden column region is shifted backwards by the number
1065 // preceding visible gaps
1066 // update hidden columns at the same time
1067 Iterator<int[]> regions = hiddenColumns.iterator();
1068 ArrayList<int[]> newhidden = new ArrayList<>();
1070 int numGapsBefore = 0;
1071 int gapPosition = 0;
1072 while (regions.hasNext())
1074 // get region coordinates accounting for gaps
1075 // we can rely on gaps not being *in* hidden regions because we already
1077 int[] region = regions.next();
1078 while (gapPosition < region[0])
1081 if (gaps.get(gapPosition))
1087 int left = region[0] - numGapsBefore;
1088 int right = region[1] - numGapsBefore;
1089 newhidden.add(new int[] { left, right });
1091 // make a string with number of gaps = length of hidden region
1092 StringBuffer sb = new StringBuffer();
1093 for (int s = 0; s < right - left + 1; s++)
1097 padGaps(sb, left, profileseq, al);
1100 hiddenColumns = newhidden;
1101 cursor.resetCursor(hiddenColumns);
1105 LOCK.writeLock().unlock();
1110 * Pad gaps in all sequences in alignment except profileseq
1113 * gap string to insert
1115 * position to insert at
1117 * sequence not to pad
1119 * alignment to pad sequences in
1121 private void padGaps(StringBuffer sb, int pos, SequenceI profileseq,
1124 // loop over the sequences and pad with gaps where required
1125 for (int s = 0, ns = al.getHeight(); s < ns; s++)
1127 SequenceI sqobj = al.getSequenceAt(s);
1128 if (sqobj != profileseq)
1130 String sq = al.getSequenceAt(s).getSequenceAsString();
1131 if (sq.length() <= pos)
1134 int diff = pos - sq.length() - 1;
1139 while ((diff = pos - sq.length() - 1) > 0)
1141 if (diff >= sb.length())
1143 sq += sb.toString();
1147 char[] buf = new char[diff];
1148 sb.getChars(0, diff, buf, 0);
1149 sq += buf.toString();
1153 sq += sb.toString();
1157 al.getSequenceAt(s).setSequence(
1158 sq.substring(0, pos) + sb.toString() + sq.substring(pos));
1165 * Returns a hashCode built from hidden column ranges
1168 public int hashCode()
1172 LOCK.readLock().lock();
1174 Iterator<int[]> it = hiddenColumns.iterator();
1175 while (it.hasNext())
1177 int[] hidden = it.next();
1178 hashCode = HASH_MULTIPLIER * hashCode + hidden[0];
1179 hashCode = HASH_MULTIPLIER * hashCode + hidden[1];
1184 LOCK.readLock().unlock();
1189 * Hide columns corresponding to the marked bits
1192 * - columns map to bits starting from zero
1194 public void hideMarkedBits(BitSet inserts)
1198 LOCK.writeLock().lock();
1199 for (int firstSet = inserts
1200 .nextSetBit(0), lastSet = 0; firstSet >= 0; firstSet = inserts
1201 .nextSetBit(lastSet))
1203 lastSet = inserts.nextClearBit(firstSet);
1204 hideColumns(firstSet, lastSet - 1);
1206 cursor.resetCursor(hiddenColumns);
1210 LOCK.writeLock().unlock();
1217 * BitSet where hidden columns will be marked
1219 public void markHiddenRegions(BitSet inserts)
1223 LOCK.readLock().lock();
1224 if (hiddenColumns == null)
1228 Iterator<int[]> it = hiddenColumns.iterator();
1229 while (it.hasNext())
1231 int[] range = it.next();
1232 inserts.set(range[0], range[1] + 1);
1236 LOCK.readLock().unlock();
1241 * Calculate the visible start and end index of an alignment.
1244 * full alignment width
1245 * @return integer array where: int[0] = startIndex, and int[1] = endIndex
1247 public int[] getVisibleStartAndEndIndex(int width)
1251 LOCK.readLock().lock();
1252 int[] alignmentStartEnd = new int[] { 0, width - 1 };
1253 int startPos = alignmentStartEnd[0];
1254 int endPos = alignmentStartEnd[1];
1256 int[] lowestRange = new int[] { -1, -1 };
1257 int[] higestRange = new int[] { -1, -1 };
1259 if (hiddenColumns == null)
1261 return new int[] { startPos, endPos };
1264 Iterator<int[]> it = hiddenColumns.iterator();
1265 while (it.hasNext())
1267 int[] range = it.next();
1268 lowestRange = (range[0] <= startPos) ? range : lowestRange;
1269 higestRange = (range[1] >= endPos) ? range : higestRange;
1272 if (lowestRange[0] == -1 && lowestRange[1] == -1)
1274 startPos = alignmentStartEnd[0];
1278 startPos = lowestRange[1] + 1;
1281 if (higestRange[0] == -1 && higestRange[1] == -1)
1283 endPos = alignmentStartEnd[1];
1287 endPos = higestRange[0] - 1;
1289 return new int[] { startPos, endPos };
1292 LOCK.readLock().unlock();
1297 * Finds the hidden region (if any) which starts or ends at res
1300 * visible residue position, unadjusted for hidden columns
1301 * @return region as [start,end] or null if no matching region is found
1303 public int[] getRegionWithEdgeAtRes(int res)
1307 LOCK.readLock().lock();
1308 int adjres = adjustForHiddenColumns(res);
1310 int[] reveal = null;
1312 if (hiddenColumns != null)
1314 // look for a region ending just before adjres
1315 int regionindex = cursor.findRegionForColumn(adjres - 1)
1317 if (regionindex < hiddenColumns.size()
1318 && hiddenColumns.get(regionindex)[1] == adjres - 1)
1320 reveal = hiddenColumns.get(regionindex);
1322 // check if the region ends just after adjres
1323 else if (regionindex < hiddenColumns.size()
1324 && hiddenColumns.get(regionindex)[0] == adjres + 1)
1326 reveal = hiddenColumns.get(regionindex);
1328 // or try the next region
1332 if (regionindex < hiddenColumns.size()
1333 && hiddenColumns.get(regionindex)[0] == adjres + 1)
1335 reveal = hiddenColumns.get(regionindex);
1343 LOCK.readLock().unlock();
1348 * Return an iterator over the hidden regions
1350 public Iterator<int[]> iterator()
1354 LOCK.readLock().lock();
1355 return new BoundedHiddenColsIterator(hiddenColumns);
1358 LOCK.readLock().unlock();
1363 * Return a bounded iterator over the hidden regions
1366 * position to start from (inclusive, absolute column position)
1368 * position to end at (inclusive, absolute column position)
1371 public Iterator<int[]> getBoundedIterator(int start, int end)
1375 LOCK.readLock().lock();
1376 return new BoundedHiddenColsIterator(start, end, hiddenColumns);
1379 LOCK.readLock().unlock();
1384 * Return a bounded iterator over the *visible* start positions of hidden
1388 * position to start from (inclusive, visible column position)
1390 * position to end at (inclusive, visible column position)
1392 public Iterator<Integer> getBoundedStartIterator(int start, int end)
1396 LOCK.readLock().lock();
1397 return new BoundedStartRegionIterator(start, end, hiddenColumns);
1400 LOCK.readLock().unlock();
1405 * Return an iterator over visible *columns* (not regions) between the given
1406 * start and end boundaries
1409 * first column (inclusive)
1411 * last column (inclusive)
1413 public Iterator<Integer> getVisibleColsIterator(int start, int end)
1417 LOCK.readLock().lock();
1418 return new VisibleColsIterator(start, end, hiddenColumns);
1421 LOCK.readLock().unlock();
1426 * return an iterator over visible segments between the given start and end
1430 * (first column inclusive from 0)
1432 * (last column - not inclusive)
1434 public Iterator<int[]> getVisContigsIterator(int start, int end)
1438 LOCK.readLock().lock();
1439 return new VisibleContigsIterator(start, end, hiddenColumns);
1442 LOCK.readLock().unlock();
1447 * return an iterator over visible segments between the given start and end
1451 * (first column - inclusive from 0)
1453 * (last column - inclusive)
1454 * @param useVisibleCoords
1455 * if true, start and end are visible column positions, not absolute
1458 public Iterator<int[]> getVisibleBlocksIterator(int start, int end,
1459 boolean useVisibleCoords)
1461 if (useVisibleCoords)
1464 // we should really just convert start and end here with
1465 // adjustForHiddenColumns
1466 // and then create a VisibleContigsIterator
1467 // but without a cursor this will be horribly slow in some situations
1468 // ... so until then...
1469 return new VisibleBlocksVisBoundsIterator(start, end, true);
1475 LOCK.readLock().lock();
1476 return new VisibleContigsIterator(start, end + 1, hiddenColumns);
1479 LOCK.readLock().unlock();
1485 * An iterator which iterates over visible regions in a range. The range is
1486 * specified in terms of visible column positions. Provides a special
1487 * "endsAtHidden" indicator to allow callers to determine if the final visible
1488 * column is adjacent to a hidden region.
1490 public class VisibleBlocksVisBoundsIterator implements Iterator<int[]>
1492 private List<int[]> vcontigs = new ArrayList<>();
1494 private int currentPosition = 0;
1496 private boolean endsAtHidden = false;
1499 * Constructor for iterator over visible regions in a range.
1502 * start position in terms of visible column position
1504 * end position in terms of visible column position
1506 * whether to use a local copy of hidden columns
1508 VisibleBlocksVisBoundsIterator(int start, int end, boolean usecopy)
1510 /* actually this implementation always uses a local copy but this may change in future */
1515 LOCK.readLock().lock();
1518 if (hiddenColumns != null && hiddenColumns.size() > 0)
1520 int blockStart = start;
1522 int hiddenSoFar = 0;
1525 // iterate until a region begins within (start,end]
1527 while ((i < hiddenColumns.size())
1528 && (hiddenColumns.get(i)[0] <= blockStart + hiddenSoFar))
1530 hiddenSoFar += hiddenColumns.get(i)[1] - hiddenColumns.get(i)[0]
1535 blockStart += hiddenSoFar; // convert start to absolute position
1536 blockEnd += hiddenSoFar; // convert end to absolute position
1538 // iterate from start to end, adding each visible region. Positions
1540 // absolute, and all hidden regions which overlap [start,end] are
1542 while (i < hiddenColumns.size()
1543 && (hiddenColumns.get(i)[0] <= blockEnd))
1545 int[] region = hiddenColumns.get(i);
1547 // end position of this visible region is either just before the
1548 // start of the next hidden region, or the absolute position of
1549 // 'end', whichever is lowest
1550 blockEnd = Math.min(blockEnd, region[0] - 1);
1552 vcontigs.add(new int[] { blockStart, blockEnd });
1554 visSoFar += blockEnd - blockStart + 1;
1556 // next visible region starts after this hidden region
1557 blockStart = region[1] + 1;
1559 hiddenSoFar += region[1] - region[0] + 1;
1561 // reset blockEnd to absolute position of 'end', assuming we've now
1562 // passed all hidden regions before end
1563 blockEnd = end + hiddenSoFar;
1567 if (visSoFar < end - start + 1)
1569 // the number of visible columns we've accounted for is less than
1570 // the number specified by end-start; work out the end position of
1571 // the last visible region
1572 blockEnd = blockStart + end - start - visSoFar;
1573 vcontigs.add(new int[] { blockStart, blockEnd });
1575 // if the last visible region ends at the next hidden region, set
1576 // endsAtHidden=true
1577 if (i < hiddenColumns.size()
1578 && hiddenColumns.get(i)[0] - 1 == blockEnd)
1580 endsAtHidden = true;
1586 // there are no hidden columns, return a single visible contig
1587 vcontigs.add(new int[] { start, end });
1588 endsAtHidden = false;
1594 LOCK.readLock().unlock();
1600 public boolean hasNext()
1602 return (currentPosition < vcontigs.size());
1608 int[] result = vcontigs.get(currentPosition);
1613 public boolean endsAtHidden()
1615 return endsAtHidden;