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();
38 * list of hidden column [start, end] ranges; the list is maintained in
39 * ascending start column order
41 private ArrayList<int[]> hiddenColumns;
46 public HiddenColumns()
55 public HiddenColumns(HiddenColumns copy)
59 LOCK.writeLock().lock();
62 if (copy.hiddenColumns != null)
64 hiddenColumns = new ArrayList<>();
65 Iterator<int[]> it = copy.iterator();
68 hiddenColumns.add(it.next());
70 cursor.resetCursor(hiddenColumns);
75 LOCK.writeLock().unlock();
80 * Copy constructor within bounds and with offset. Copies hidden column
81 * regions fully contained between start and end, and offsets positions by
85 * HiddenColumns instance to copy from
87 * lower bound to copy from
89 * upper bound to copy to
91 * offset to subtract from each region boundary position
94 public HiddenColumns(HiddenColumns copy, int start, int end, int offset)
98 LOCK.writeLock().lock();
101 hiddenColumns = new ArrayList<>();
102 Iterator<int[]> it = copy.getBoundedIterator(start, end);
105 int[] region = it.next();
106 // still need to check boundaries because iterator returns
107 // all overlapping regions and we need contained regions
108 if (region[0] >= start && region[1] <= end)
112 { region[0] - offset, region[1] - offset });
115 cursor.resetCursor(hiddenColumns);
119 LOCK.writeLock().unlock();
124 * Output regions data as a string. String is in the format:
125 * reg0[0]<between>reg0[1]<delimiter>reg1[0]<between>reg1[1] ... regn[1]
128 * string to delimit regions
129 * @param betweenstring
130 * to put between start and end region values
131 * @return regions formatted according to delimiter and between strings
133 public String regionsToString(String delimiter, String between)
137 LOCK.readLock().lock();
138 StringBuilder regionBuilder = new StringBuilder();
139 if (hiddenColumns != null)
141 Iterator<int[]> it = hiddenColumns.iterator();
144 int[] range = it.next();
145 regionBuilder.append(delimiter).append(range[0]).append(between)
149 regionBuilder.deleteCharAt(0);
153 return regionBuilder.toString();
156 LOCK.readLock().unlock();
161 * Find the number of hidden columns
163 * @return number of hidden columns
169 LOCK.readLock().lock();
171 if (hiddenColumns != null)
173 Iterator<int[]> it = hiddenColumns.iterator();
176 int[] range = it.next();
177 size += range[1] - range[0] + 1;
183 LOCK.readLock().unlock();
188 * Get the number of distinct hidden regions
190 * @return number of regions
192 public int getNumberOfRegions()
196 LOCK.readLock().lock();
198 if (hasHiddenColumns())
200 num = hiddenColumns.size();
205 LOCK.readLock().unlock();
210 public boolean equals(Object obj)
214 LOCK.readLock().lock();
216 if (!(obj instanceof HiddenColumns))
220 HiddenColumns that = (HiddenColumns) obj;
223 * check hidden columns are either both null, or match
225 if (this.hiddenColumns == null)
227 return (that.hiddenColumns == null);
229 if (that.hiddenColumns == null
230 || that.hiddenColumns.size() != this.hiddenColumns.size())
235 Iterator<int[]> it = hiddenColumns.iterator();
236 Iterator<int[]> thatit = that.iterator();
239 int[] thisRange = it.next();
240 int[] thatRange = thatit.next();
241 if (thisRange[0] != thatRange[0] || thisRange[1] != thatRange[1])
249 LOCK.readLock().unlock();
254 * Return absolute column index for a visible column index
257 * int column index in alignment view (count from zero)
258 * @return alignment column index for column
260 public int adjustForHiddenColumns(int column)
264 LOCK.readLock().lock();
267 if (hiddenColumns != null)
269 result += cursor.getHiddenOffset(column);
275 LOCK.readLock().unlock();
280 * Use this method to find out where a column will appear in the visible
281 * alignment when hidden columns exist. If the column is not visible, then the
282 * left-most visible column will always be returned.
284 * @param hiddenColumn
285 * the column index in the full alignment including hidden columns
286 * @return the position of the column in the visible alignment
288 public int findColumnPosition(int hiddenColumn)
292 LOCK.readLock().lock();
293 int result = hiddenColumn;
295 if (hiddenColumns != null)
297 int index = cursor.findRegionForColumn(hiddenColumn);
298 int hiddenBeforeCol = cursor.getHiddenSoFar();
300 // just subtract hidden cols count - this works fine if column is
302 result = hiddenColumn - hiddenBeforeCol;
304 // now check in case column is hidden - it will be in the returned
306 if (index < hiddenColumns.size())
308 int[] region = hiddenColumns.get(index);
309 if (hiddenColumn >= region[0] && hiddenColumn <= region[1])
311 // actually col is hidden, return region[0]-1
312 // unless region[0]==0 in which case return 0
319 result = region[0] - 1 - hiddenBeforeCol;
325 return result; // return the shifted position after removing hidden
329 LOCK.readLock().unlock();
334 * Find the visible column which is a given visible number of columns to the
335 * left of another visible column. i.e. for a startColumn x, the column which
336 * is distance 1 away will be column x-1.
338 * @param visibleDistance
339 * the number of visible columns to offset by
341 * the column to start from
342 * @return the position of the column in the visible alignment
344 public int subtractVisibleColumns(int visibleDistance, int startColumn)
348 LOCK.readLock().lock();
349 int distance = visibleDistance;
351 // in case startColumn is in a hidden region, move it to the left
352 int start = adjustForHiddenColumns(findColumnPosition(startColumn));
354 Iterator<int[]> it = new ReverseRegionsIterator(0, start,
357 while (it.hasNext() && (distance > 0))
359 int[] region = it.next();
361 if (start > region[1])
363 // subtract the gap to right of region from distance
364 if (start - region[1] <= distance)
366 distance -= start - region[1];
367 start = region[0] - 1;
371 start = start - distance;
377 return start - distance;
381 LOCK.readLock().unlock();
386 * This method returns the rightmost limit of a region of an alignment with
387 * hidden columns. In otherwords, the next hidden column.
390 * the absolute (visible) alignmentPosition to find the next hidden
393 public int getHiddenBoundaryRight(int alPos)
397 LOCK.readLock().lock();
398 if (hiddenColumns != null)
400 int index = cursor.findRegionForColumn(alPos);
401 if (index < hiddenColumns.size())
403 int[] region = hiddenColumns.get(index);
404 if (alPos < region[0])
408 else if ((alPos <= region[1])
409 && (index + 1 < hiddenColumns.size()))
411 // alPos is within a hidden region, return the next one
413 region = hiddenColumns.get(index + 1);
421 LOCK.readLock().unlock();
426 * This method returns the leftmost limit of a region of an alignment with
427 * hidden columns. In otherwords, the previous hidden column.
430 * the absolute (visible) alignmentPosition to find the previous
433 public int getHiddenBoundaryLeft(int alPos)
437 LOCK.readLock().lock();
439 if (hiddenColumns != null)
441 int index = cursor.findRegionForColumn(alPos);
445 int[] region = hiddenColumns.get(index - 1);
452 LOCK.readLock().unlock();
457 * Adds the specified column range to the hidden columns collection
460 * start of range to add (absolute position in alignment)
462 * end of range to add (absolute position in alignment)
464 public void hideColumns(int start, int end)
466 boolean wasAlreadyLocked = false;
469 // check if the write lock was already locked by this thread,
470 // as this method can be called internally in loops within HiddenColumns
471 if (!LOCK.isWriteLockedByCurrentThread())
473 LOCK.writeLock().lock();
477 wasAlreadyLocked = true;
480 if (hiddenColumns == null)
482 hiddenColumns = new ArrayList<>();
486 * new range follows everything else; check first to avoid looping over whole hiddenColumns collection
488 if (hiddenColumns.isEmpty()
489 || start > hiddenColumns.get(hiddenColumns.size() - 1)[1])
491 hiddenColumns.add(new int[] { start, end });
496 * traverse existing hidden ranges and insert / amend / append as
499 boolean added = false;
500 for (int i = 0; !added && i < hiddenColumns.size(); i++)
502 added = insertRangeAtRegion(i, start, end);
505 if (!wasAlreadyLocked)
507 cursor.resetCursor(hiddenColumns);
511 if (!wasAlreadyLocked)
513 LOCK.writeLock().unlock();
519 * Insert [start, range] at the region at index i in hiddenColumns, if
525 * start of range to insert
527 * end of range to insert
528 * @return true if range was successfully inserted
530 private boolean insertRangeAtRegion(int i, int start, int end)
532 boolean added = false;
534 int[] region = hiddenColumns.get(i);
535 if (end < region[0] - 1)
538 * insert discontiguous preceding range
540 hiddenColumns.add(i, new int[] { start, end });
543 else if (end <= region[1])
546 * new range overlaps existing, or is contiguous preceding it - adjust
549 region[0] = Math.min(region[0], start);
552 else if (start <= region[1] + 1)
555 * new range overlaps existing, or is contiguous following it - adjust
556 * start and end columns
558 region[0] = Math.min(region[0], start);
559 region[1] = Math.max(region[1], end);
562 * also update or remove any subsequent ranges
563 * that are overlapped
565 while (i < hiddenColumns.size() - 1)
567 int[] nextRegion = hiddenColumns.get(i + 1);
568 if (nextRegion[0] > end + 1)
571 * gap to next hidden range - no more to update
575 region[1] = Math.max(nextRegion[1], end);
576 hiddenColumns.subList(i + 1, i + 2).clear();
584 * Answers if a column in the alignment is visible
587 * absolute position of column in the alignment
588 * @return true if column is visible
590 public boolean isVisible(int column)
594 LOCK.readLock().lock();
596 Iterator<int[]> it = new RegionsIterator(column, column,
597 hiddenColumns, cursor);
600 int[] region = it.next();
601 if (column >= region[0] && column <= region[1])
610 LOCK.readLock().unlock();
615 * Get the visible sections of a set of sequences
618 * sequence position to start from
620 * sequence position to end at
622 * an array of sequences
623 * @return an array of strings encoding the visible parts of each sequence
625 public String[] getVisibleSequenceStrings(int start, int end,
630 LOCK.readLock().lock();
631 int iSize = seqs.length;
632 String[] selections = new String[iSize];
633 if (hiddenColumns != null && hiddenColumns.size() > 0)
635 for (int i = 0; i < iSize; i++)
637 StringBuffer visibleSeq = new StringBuffer();
639 Iterator<int[]> blocks = new VisibleContigsIterator(start,
640 end + 1, hiddenColumns);
642 while (blocks.hasNext())
644 int[] block = blocks.next();
645 if (blocks.hasNext())
648 .append(seqs[i].getSequence(block[0], block[1] + 1));
653 .append(seqs[i].getSequence(block[0], block[1]));
657 selections[i] = visibleSeq.toString();
662 for (int i = 0; i < iSize; i++)
664 selections[i] = seqs[i].getSequenceAsString(start, end);
671 LOCK.readLock().unlock();
676 * Locate the first position visible for this sequence. If seq isn't visible
677 * then return the position of the left side of the hidden boundary region.
680 * sequence to find position for
681 * @return visible start position
683 public int locateVisibleStartOfSequence(SequenceI seq)
687 LOCK.readLock().lock();
690 if (hiddenColumns == null || hiddenColumns.size() == 0)
692 return seq.findIndex(seq.getStart()) - 1;
695 // Simply walk along the sequence whilst watching for hidden column
697 Iterator<int[]> regions = hiddenColumns.iterator();
698 int hideStart = seq.getLength();
702 boolean foundStart = false;
704 // step through the non-gapped positions of the sequence
705 for (int i = seq.getStart(); i <= seq.getEnd() && (!foundStart); i++)
707 // get alignment position of this residue in the sequence
708 int p = seq.findIndex(i) - 1;
710 // update hidden region start/end
711 while (hideEnd < p && regions.hasNext())
713 int[] region = regions.next();
715 visNext += region[0] - visPrev;
716 hideStart = region[0];
721 hideStart = seq.getLength();
723 // update visible boundary for sequence
733 return findColumnPosition(start);
735 // otherwise, sequence was completely hidden
739 LOCK.readLock().unlock();
744 * delete any columns in alignmentAnnotation that are hidden (including
745 * sequence associated annotation).
747 * @param alignmentAnnotation
749 public void makeVisibleAnnotation(AlignmentAnnotation alignmentAnnotation)
751 makeVisibleAnnotation(0, alignmentAnnotation.annotations.length,
752 alignmentAnnotation);
756 * delete any columns in alignmentAnnotation that are hidden (including
757 * sequence associated annotation).
760 * remove any annotation to the right of this column
762 * remove any annotation to the left of this column
763 * @param alignmentAnnotation
764 * the annotation to operate on
766 public void makeVisibleAnnotation(int start, int end,
767 AlignmentAnnotation alignmentAnnotation)
771 LOCK.readLock().lock();
773 int startFrom = start;
776 if (alignmentAnnotation.annotations != null)
778 if (hiddenColumns != null && hiddenColumns.size() > 0)
780 removeHiddenAnnotation(startFrom, endAt, alignmentAnnotation);
784 alignmentAnnotation.restrict(startFrom, endAt);
789 LOCK.readLock().unlock();
793 private void removeHiddenAnnotation(int start, int end,
794 AlignmentAnnotation alignmentAnnotation)
796 // mangle the alignmentAnnotation annotation array
797 ArrayList<Annotation[]> annels = new ArrayList<>();
798 Annotation[] els = null;
802 Iterator<int[]> blocks = new VisibleContigsIterator(start, end + 1,
806 int annotationLength;
807 while (blocks.hasNext())
809 int[] block = blocks.next();
810 annotationLength = block[1] - block[0] + 1;
812 if (blocks.hasNext())
814 // copy just the visible segment of the annotation row
815 copylength = annotationLength;
819 if (annotationLength + block[0] <= alignmentAnnotation.annotations.length)
821 // copy just the visible segment of the annotation row
822 copylength = annotationLength;
826 // copy to the end of the annotation row
827 copylength = alignmentAnnotation.annotations.length - block[0];
831 els = new Annotation[annotationLength];
833 System.arraycopy(alignmentAnnotation.annotations, block[0], els, 0,
835 w += annotationLength;
840 alignmentAnnotation.annotations = new Annotation[w];
843 for (Annotation[] chnk : annels)
845 System.arraycopy(chnk, 0, alignmentAnnotation.annotations, w,
854 * @return true if there are columns hidden
856 public boolean hasHiddenColumns()
860 LOCK.readLock().lock();
861 return hiddenColumns != null && hiddenColumns.size() > 0;
864 LOCK.readLock().unlock();
870 * @return true if there are more than one set of columns hidden
872 public boolean hasManyHiddenColumns()
876 LOCK.readLock().lock();
877 return hiddenColumns != null && hiddenColumns.size() > 1;
880 LOCK.readLock().unlock();
885 * mark the columns corresponding to gap characters as hidden in the column
890 public void hideInsertionsFor(SequenceI sr)
894 LOCK.writeLock().lock();
895 List<int[]> inserts = sr.getInsertions();
896 for (int[] r : inserts)
898 hideColumns(r[0], r[1]);
900 cursor.resetCursor(hiddenColumns);
903 LOCK.writeLock().unlock();
908 * Unhides, and adds to the selection list, all hidden columns
910 public void revealAllHiddenColumns(ColumnSelection sel)
914 LOCK.writeLock().lock();
915 if (hiddenColumns != null)
917 Iterator<int[]> it = hiddenColumns.iterator();
920 int[] region = it.next();
921 for (int j = region[0]; j < region[1] + 1; j++)
926 hiddenColumns = null;
927 cursor.resetCursor(hiddenColumns);
931 LOCK.writeLock().unlock();
936 * Reveals, and marks as selected, the hidden column range with the given
941 public void revealHiddenColumns(int start, ColumnSelection sel)
945 LOCK.writeLock().lock();
947 if (hiddenColumns != null)
949 int regionIndex = cursor.findRegionForColumn(start);
951 if (regionIndex != -1 && regionIndex != hiddenColumns.size())
953 // regionIndex is the region which either contains start
954 // or lies to the right of start
955 int[] region = hiddenColumns.get(regionIndex);
956 if (start == region[0])
958 for (int j = region[0]; j < region[1] + 1; j++)
962 hiddenColumns.remove(regionIndex);
964 if (hiddenColumns.isEmpty())
966 hiddenColumns = null;
968 cursor.updateForDeletedRegion(hiddenColumns);
974 LOCK.writeLock().unlock();
979 * Add gaps into the sequences aligned to profileseq under the given
984 * - alignment to have gaps inserted into it
986 * - alignment view where sequence corresponding to profileseq is
988 * @return new HiddenColumns for new alignment view, with insertions into
989 * profileseq marked as hidden.
991 public static HiddenColumns propagateInsertions(SequenceI profileseq,
992 AlignmentI al, AlignmentView input)
996 char gc = al.getGapCharacter();
997 Object[] alandhidden = input.getAlignmentAndHiddenColumns(gc);
998 HiddenColumns nview = (HiddenColumns) alandhidden[1];
999 SequenceI origseq = ((SequenceI[]) alandhidden[0])[profsqpos];
1000 nview.propagateInsertions(profileseq, al, origseq);
1007 * - sequence in al which corresponds to origseq
1009 * - alignment which is to have gaps inserted into it
1011 * - sequence corresponding to profileseq which defines gap map for
1014 private void propagateInsertions(SequenceI profileseq, AlignmentI al,
1019 LOCK.writeLock().lock();
1021 char gc = al.getGapCharacter();
1023 // take the set of hidden columns, and the set of gaps in origseq,
1024 // and remove all the hidden gaps from hiddenColumns
1026 // first get the gaps as a Bitset
1027 BitSet gaps = origseq.gapBitset();
1029 // now calculate hidden ^ not(gap)
1030 BitSet hidden = new BitSet();
1031 markHiddenRegions(hidden);
1032 hidden.andNot(gaps);
1033 hiddenColumns = null;
1034 this.hideMarkedBits(hidden);
1036 // for each sequence in the alignment, except the profile sequence,
1037 // insert gaps corresponding to each hidden region
1038 // but where each hidden column region is shifted backwards by the number
1040 // preceding visible gaps
1041 // update hidden columns at the same time
1042 Iterator<int[]> regions = hiddenColumns.iterator();
1043 ArrayList<int[]> newhidden = new ArrayList<>();
1045 int numGapsBefore = 0;
1046 int gapPosition = 0;
1047 while (regions.hasNext())
1049 // get region coordinates accounting for gaps
1050 // we can rely on gaps not being *in* hidden regions because we already
1052 int[] region = regions.next();
1053 while (gapPosition < region[0])
1056 if (gaps.get(gapPosition))
1062 int left = region[0] - numGapsBefore;
1063 int right = region[1] - numGapsBefore;
1064 newhidden.add(new int[] { left, right });
1066 // make a string with number of gaps = length of hidden region
1067 StringBuffer sb = new StringBuffer();
1068 for (int s = 0; s < right - left + 1; s++)
1072 padGaps(sb, left, profileseq, al);
1075 hiddenColumns = newhidden;
1076 cursor.resetCursor(hiddenColumns);
1079 LOCK.writeLock().unlock();
1084 * Pad gaps in all sequences in alignment except profileseq
1087 * gap string to insert
1089 * position to insert at
1091 * sequence not to pad
1093 * alignment to pad sequences in
1095 private void padGaps(StringBuffer sb, int pos, SequenceI profileseq,
1098 // loop over the sequences and pad with gaps where required
1099 for (int s = 0, ns = al.getHeight(); s < ns; s++)
1101 SequenceI sqobj = al.getSequenceAt(s);
1102 if (sqobj != profileseq)
1104 String sq = al.getSequenceAt(s).getSequenceAsString();
1105 if (sq.length() <= pos)
1108 int diff = pos - sq.length() - 1;
1113 while ((diff = pos - sq.length() - 1) > 0)
1115 if (diff >= sb.length())
1117 sq += sb.toString();
1121 char[] buf = new char[diff];
1122 sb.getChars(0, diff, buf, 0);
1123 sq += buf.toString();
1127 sq += sb.toString();
1131 al.getSequenceAt(s).setSequence(
1132 sq.substring(0, pos) + sb.toString() + sq.substring(pos));
1139 * Returns a hashCode built from hidden column ranges
1142 public int hashCode()
1146 LOCK.readLock().lock();
1148 Iterator<int[]> it = hiddenColumns.iterator();
1149 while (it.hasNext())
1151 int[] hidden = it.next();
1152 hashCode = HASH_MULTIPLIER * hashCode + hidden[0];
1153 hashCode = HASH_MULTIPLIER * hashCode + hidden[1];
1158 LOCK.readLock().unlock();
1163 * Hide columns corresponding to the marked bits
1166 * - columns map to bits starting from zero
1168 public void hideMarkedBits(BitSet inserts)
1172 LOCK.writeLock().lock();
1173 for (int firstSet = inserts
1174 .nextSetBit(0), lastSet = 0; firstSet >= 0; firstSet = inserts
1175 .nextSetBit(lastSet))
1177 lastSet = inserts.nextClearBit(firstSet);
1178 hideColumns(firstSet, lastSet - 1);
1180 cursor.resetCursor(hiddenColumns);
1183 LOCK.writeLock().unlock();
1190 * BitSet where hidden columns will be marked
1192 public void markHiddenRegions(BitSet inserts)
1196 LOCK.readLock().lock();
1197 if (hiddenColumns == null)
1201 Iterator<int[]> it = hiddenColumns.iterator();
1202 while (it.hasNext())
1204 int[] range = it.next();
1205 inserts.set(range[0], range[1] + 1);
1209 LOCK.readLock().unlock();
1214 * Calculate the visible start and end index of an alignment.
1217 * full alignment width
1218 * @return integer array where: int[0] = startIndex, and int[1] = endIndex
1220 public int[] getVisibleStartAndEndIndex(int width)
1224 LOCK.readLock().lock();
1225 int[] alignmentStartEnd = new int[] { 0, width - 1 };
1226 int startPos = alignmentStartEnd[0];
1227 int endPos = alignmentStartEnd[1];
1229 int[] lowestRange = new int[] { -1, -1 };
1230 int[] higestRange = new int[] { -1, -1 };
1232 if (hiddenColumns == null)
1234 return new int[] { startPos, endPos };
1237 Iterator<int[]> it = hiddenColumns.iterator();
1238 while (it.hasNext())
1240 int[] range = it.next();
1241 lowestRange = (range[0] <= startPos) ? range : lowestRange;
1242 higestRange = (range[1] >= endPos) ? range : higestRange;
1245 if (lowestRange[0] == -1 && lowestRange[1] == -1)
1247 startPos = alignmentStartEnd[0];
1251 startPos = lowestRange[1] + 1;
1254 if (higestRange[0] == -1 && higestRange[1] == -1)
1256 endPos = alignmentStartEnd[1];
1260 endPos = higestRange[0] - 1;
1262 return new int[] { startPos, endPos };
1265 LOCK.readLock().unlock();
1270 * Finds the hidden region (if any) which starts or ends at res
1273 * visible residue position, unadjusted for hidden columns
1274 * @return region as [start,end] or null if no matching region is found
1276 public int[] getRegionWithEdgeAtRes(int res)
1280 LOCK.readLock().lock();
1281 int adjres = adjustForHiddenColumns(res);
1283 int[] reveal = null;
1285 if (hiddenColumns != null)
1287 int regionindex = cursor.findRegionForColumn(adjres - 1);
1288 if (regionindex < hiddenColumns.size()
1289 && hiddenColumns.get(regionindex)[1] == adjres - 1)
1291 reveal = hiddenColumns.get(regionindex);
1295 regionindex = cursor.findRegionForColumn(adjres + 1);
1296 if (regionindex < hiddenColumns.size()
1297 && hiddenColumns.get(regionindex)[0] == adjres + 1)
1299 reveal = hiddenColumns.get(regionindex);
1307 LOCK.readLock().unlock();
1312 * Return an iterator over the hidden regions
1314 public Iterator<int[]> iterator()
1318 LOCK.readLock().lock();
1319 return new BoundedHiddenColsIterator(hiddenColumns);
1322 LOCK.readLock().unlock();
1327 * Return a bounded iterator over the hidden regions
1330 * position to start from (inclusive, absolute column position)
1332 * position to end at (inclusive, absolute column position)
1335 public Iterator<int[]> getBoundedIterator(int start, int end)
1339 LOCK.readLock().lock();
1340 return new BoundedHiddenColsIterator(start, end, hiddenColumns);
1343 LOCK.readLock().unlock();
1348 * Return a bounded iterator over the *visible* start positions of hidden
1352 * position to start from (inclusive, visible column position)
1354 * position to end at (inclusive, visible column position)
1356 public Iterator<Integer> getBoundedStartIterator(int start, int end)
1360 LOCK.readLock().lock();
1361 return new BoundedStartRegionIterator(start, end, hiddenColumns);
1364 LOCK.readLock().unlock();
1369 * Return an iterator over visible *columns* (not regions) between the given
1370 * start and end boundaries
1373 * first column (inclusive)
1375 * last column (inclusive)
1377 public Iterator<Integer> getVisibleColsIterator(int start, int end)
1381 LOCK.readLock().lock();
1382 return new VisibleColsIterator(start, end, hiddenColumns);
1385 LOCK.readLock().unlock();
1390 * return an iterator over visible segments between the given start and end
1394 * (first column inclusive from 0)
1396 * (last column - not inclusive)
1398 public Iterator<int[]> getVisContigsIterator(int start, int end)
1402 LOCK.readLock().lock();
1403 return new VisibleContigsIterator(start, end, hiddenColumns);
1406 LOCK.readLock().unlock();
1411 * return an iterator over visible segments between the given start and end
1415 * (first column - inclusive from 0)
1417 * (last column - inclusive)
1418 * @param useVisibleCoords
1419 * if true, start and end are visible column positions, not absolute
1422 public Iterator<int[]> getVisibleBlocksIterator(int start, int end,
1423 boolean useVisibleCoords)
1425 if (useVisibleCoords)
1428 // we should really just convert start and end here with
1429 // adjustForHiddenColumns
1430 // and then create a VisibleContigsIterator
1431 // but without a cursor this will be horribly slow in some situations
1432 // ... so until then...
1433 return new VisibleBlocksVisBoundsIterator(start, end, true);
1439 LOCK.readLock().lock();
1440 return new VisibleContigsIterator(start, end + 1, hiddenColumns);
1443 LOCK.readLock().unlock();
1449 * An iterator which iterates over visible regions in a range. The range is
1450 * specified in terms of visible column positions. Provides a special
1451 * "endsAtHidden" indicator to allow callers to determine if the final visible
1452 * column is adjacent to a hidden region.
1454 public class VisibleBlocksVisBoundsIterator implements Iterator<int[]>
1456 private List<int[]> vcontigs = new ArrayList<>();
1458 private int currentPosition = 0;
1460 private boolean endsAtHidden = false;
1463 * Constructor for iterator over visible regions in a range.
1466 * start position in terms of visible column position
1468 * end position in terms of visible column position
1470 * whether to use a local copy of hidden columns
1472 VisibleBlocksVisBoundsIterator(int start, int end, boolean usecopy)
1474 /* actually this implementation always uses a local copy but this may change in future */
1479 LOCK.readLock().lock();
1482 if (hiddenColumns != null && hiddenColumns.size() > 0)
1484 int blockStart = start;
1486 int hiddenSoFar = 0;
1489 // iterate until a region begins within (start,end]
1491 while ((i < hiddenColumns.size())
1492 && (hiddenColumns.get(i)[0] <= blockStart + hiddenSoFar))
1494 hiddenSoFar += hiddenColumns.get(i)[1] - hiddenColumns.get(i)[0]
1499 blockStart += hiddenSoFar; // convert start to absolute position
1500 blockEnd += hiddenSoFar; // convert end to absolute position
1502 // iterate from start to end, adding each visible region. Positions
1504 // absolute, and all hidden regions which overlap [start,end] are
1506 while (i < hiddenColumns.size()
1507 && (hiddenColumns.get(i)[0] <= blockEnd))
1509 int[] region = hiddenColumns.get(i);
1511 // end position of this visible region is either just before the
1512 // start of the next hidden region, or the absolute position of
1513 // 'end', whichever is lowest
1514 blockEnd = Math.min(blockEnd, region[0] - 1);
1516 vcontigs.add(new int[] { blockStart, blockEnd });
1518 visSoFar += blockEnd - blockStart + 1;
1520 // next visible region starts after this hidden region
1521 blockStart = region[1] + 1;
1523 hiddenSoFar += region[1] - region[0] + 1;
1525 // reset blockEnd to absolute position of 'end', assuming we've now
1526 // passed all hidden regions before end
1527 blockEnd = end + hiddenSoFar;
1531 if (visSoFar < end - start + 1)
1533 // the number of visible columns we've accounted for is less than
1534 // the number specified by end-start; work out the end position of
1535 // the last visible region
1536 blockEnd = blockStart + end - start - visSoFar;
1537 vcontigs.add(new int[] { blockStart, blockEnd });
1539 // if the last visible region ends at the next hidden region, set
1540 // endsAtHidden=true
1541 if (i < hiddenColumns.size()
1542 && hiddenColumns.get(i)[0] - 1 == blockEnd)
1544 endsAtHidden = true;
1550 // there are no hidden columns, return a single visible contig
1551 vcontigs.add(new int[] { start, end });
1552 endsAtHidden = false;
1558 LOCK.readLock().unlock();
1564 public boolean hasNext()
1566 return (currentPosition < vcontigs.size());
1572 int[] result = vcontigs.get(currentPosition);
1577 public boolean endsAtHidden()
1579 return endsAtHidden;