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 jalview.util.Comparison;
25 import java.util.ArrayList;
26 import java.util.BitSet;
27 import java.util.Collections;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.Vector;
31 import java.util.concurrent.locks.ReentrantReadWriteLock;
33 public class HiddenColumns
35 private static final int HASH_MULTIPLIER = 31;
37 private static final ReentrantReadWriteLock LOCK = new ReentrantReadWriteLock();
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 = copy.copyHiddenRegionsToArrayList(0);
71 LOCK.writeLock().unlock();
76 * Copy constructor within bounds and with offset. Copies hidden column
77 * regions fully contained between start and end, and offsets positions by
81 * HiddenColumns instance to copy from
83 * lower bound to copy from
85 * upper bound to copy to
87 * offset to subtract from each region boundary position
90 public HiddenColumns(HiddenColumns copy, int start, int end, int offset)
94 LOCK.writeLock().lock();
97 hiddenColumns = new ArrayList<>();
98 Iterator<int[]> it = copy.getBoundedIterator(start, end, true);
101 int[] region = it.next();
102 // still need to check boundaries because iterator returns
103 // all overlapping regions and we need contained regions
104 if (region[0] >= start && region[1] <= end)
108 { region[0] - offset, region[1] - offset });
114 LOCK.writeLock().unlock();
119 * This method is used to return all the HiddenColumn regions and is intended
120 * to remain private. External callers which need a copy of the regions can
121 * call getHiddenColumnsCopyAsList.
123 * @return empty list or List of hidden column intervals
125 private List<int[]> getHiddenRegions()
127 return hiddenColumns == null ? Collections.<int[]> emptyList()
132 * Output regions data as a string. String is in the format:
133 * reg0[0]<between>reg0[1]<delimiter>reg1[0]<between>reg1[1] ... regn[1]
136 * string to delimit regions
137 * @param betweenstring
138 * to put between start and end region values
139 * @return regions formatted according to delimiter and between strings
141 public String regionsToString(String delimiter, String between)
145 LOCK.readLock().lock();
146 StringBuilder regionBuilder = new StringBuilder();
147 if (hiddenColumns != null)
149 for (int[] range : hiddenColumns)
151 regionBuilder.append(delimiter).append(range[0]).append(between)
155 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 (hasHiddenColumns())
177 for (int[] range : hiddenColumns)
179 size += range[1] - range[0] + 1;
185 LOCK.readLock().unlock();
190 * Get the number of distinct hidden regions
192 * @return number of regions
194 public int getNumberOfRegions()
198 LOCK.readLock().lock();
200 if (hasHiddenColumns())
202 num = hiddenColumns.size();
207 LOCK.readLock().unlock();
212 public boolean equals(Object obj)
216 LOCK.readLock().lock();
218 if (!(obj instanceof HiddenColumns))
222 HiddenColumns that = (HiddenColumns) obj;
225 * check hidden columns are either both null, or match
227 if (this.hiddenColumns == null)
229 return (that.hiddenColumns == null);
231 if (that.hiddenColumns == null
232 || that.hiddenColumns.size() != this.hiddenColumns.size())
237 for (int[] thisRange : hiddenColumns)
239 int[] thatRange = that.hiddenColumns.get(i++);
240 if (thisRange[0] != thatRange[0] || thisRange[1] != thatRange[1])
248 LOCK.readLock().unlock();
253 * Return absolute column index for a visible column index
256 * int column index in alignment view (count from zero)
257 * @return alignment column index for column
259 public int adjustForHiddenColumns(int column)
263 LOCK.readLock().lock();
265 if (hiddenColumns != null)
267 for (int i = 0; i < hiddenColumns.size(); i++)
269 int[] region = hiddenColumns.get(i);
270 if (result >= region[0])
272 result += region[1] - region[0] + 1;
279 LOCK.readLock().unlock();
284 * Use this method to find out where a column will appear in the visible
285 * alignment when hidden columns exist. If the column is not visible, then the
286 * left-most visible column will always be returned.
288 * @param hiddenColumn
289 * the column index in the full alignment including hidden columns
290 * @return the position of the column in the visible alignment
292 public int findColumnPosition(int hiddenColumn)
296 LOCK.readLock().lock();
297 int result = hiddenColumn;
298 if (hiddenColumns != null)
304 region = hiddenColumns.get(index++);
305 if (hiddenColumn > region[1])
307 result -= region[1] + 1 - region[0];
309 } while ((hiddenColumn > region[1])
310 && (index < hiddenColumns.size()));
312 if (hiddenColumn >= region[0] && hiddenColumn <= region[1])
314 // Here the hidden column is within a region, so
315 // we want to return the position of region[0]-1, adjusted for any
316 // earlier hidden columns.
317 // Calculate the difference between the actual hidden col position
318 // and region[0]-1, and then subtract from result to convert result
320 // the adjusted hiddenColumn value to the adjusted region[0]-1 value
322 // However, if the region begins at 0 we cannot return region[0]-1
330 return result - (hiddenColumn - region[0] + 1);
334 return result; // return the shifted position after removing hidden
338 LOCK.readLock().unlock();
343 * Find the visible column which is a given visible number of columns to the
344 * left of another visible column. i.e. for a startColumn x, the column which
345 * is distance 1 away will be column x-1.
347 * @param visibleDistance
348 * the number of visible columns to offset by
350 * the column to start from
351 * @return the position of the column in the visible alignment
353 public int subtractVisibleColumns(int visibleDistance, int startColumn)
358 LOCK.readLock().lock();
359 int distance = visibleDistance;
361 // in case startColumn is in a hidden region, move it to the left
362 int start = adjustForHiddenColumns(findColumnPosition(startColumn));
364 // get index of hidden region to left of start
365 int index = getHiddenIndexLeft(start);
368 // no hidden regions to left of startColumn
369 return start - distance;
372 // walk backwards through the alignment subtracting the counts of visible
373 // columns from distance
376 int nextstart = start;
378 while ((index > -1) && (distance - gap > 0))
380 // subtract the gap to right of region from distance
384 // calculate the next gap
385 region = hiddenColumns.get(index);
386 gap = start - region[1];
388 // set start to just to left of current region
389 nextstart = region[0] - 1;
393 if (distance - gap > 0)
395 // fell out of loop because there are no more hidden regions
397 return nextstart - distance;
399 return start - distance;
402 LOCK.readLock().unlock();
408 * Use this method to determine the set of hiddenRegion start positions
409 * between absolute position <start> and absolute position <end>
412 * absolute residue to start from
414 * absolute residue to end at
416 * @return list of column numbers in *visible* view where hidden regions start
418 public List<Integer> findHiddenRegionPositions(int start, int end)
422 LOCK.readLock().lock();
423 List<Integer> positions = null;
425 if (hiddenColumns != null)
427 positions = new ArrayList<>(hiddenColumns.size());
429 // navigate to start, keeping count of hidden columns
432 while ((i < hiddenColumns.size())
433 && (hiddenColumns.get(i)[0] < start))
435 int[] region = hiddenColumns.get(i);
436 hiddenSoFar += region[1] - region[0] + 1;
440 // iterate from start to end, adding start positions of each
441 // hidden region. Positions are visible columns count, not absolute
442 while (i < hiddenColumns.size()
443 && (hiddenColumns.get(i)[0] < end))
445 int[] region = hiddenColumns.get(i);
446 positions.add(region[0] - hiddenSoFar);
447 hiddenSoFar += region[1] - region[0] + 1;
453 positions = new ArrayList<>();
459 LOCK.readLock().unlock();
464 * This method returns the rightmost limit of a region of an alignment with
465 * hidden columns. In otherwords, the next hidden column.
470 public int getHiddenBoundaryRight(int alPos)
474 LOCK.readLock().lock();
475 if (hiddenColumns != null)
480 int[] region = hiddenColumns.get(index);
481 if (alPos < region[0])
487 } while (index < hiddenColumns.size());
493 LOCK.readLock().unlock();
499 * This method returns the leftmost limit of a region of an alignment with
500 * hidden columns. In otherwords, the previous hidden column.
505 public int getHiddenBoundaryLeft(int alPos)
509 LOCK.readLock().lock();
511 if (hiddenColumns != null)
513 int index = hiddenColumns.size() - 1;
516 int[] region = hiddenColumns.get(index);
517 if (alPos > region[1])
523 } while (index > -1);
529 LOCK.readLock().unlock();
534 * This method returns the index of the hidden region to the left of a column
535 * position. If the column is in a hidden region it returns the index of the
536 * region to the left. If there is no hidden region to the left it returns -1.
541 private int getHiddenIndexLeft(int pos)
546 LOCK.readLock().lock();
547 if (hiddenColumns != null)
549 int index = hiddenColumns.size() - 1;
552 int[] region = hiddenColumns.get(index);
559 } while (index > -1);
565 LOCK.readLock().unlock();
571 * Adds the specified column range to the hidden columns
576 public void hideColumns(int start, int end)
578 boolean wasAlreadyLocked = false;
581 // check if the write lock was already locked by this thread,
582 // as this method can be called internally in loops within HiddenColumns
583 if (!LOCK.isWriteLockedByCurrentThread())
585 LOCK.writeLock().lock();
589 wasAlreadyLocked = true;
592 if (hiddenColumns == null)
594 hiddenColumns = new ArrayList<>();
598 * traverse existing hidden ranges and insert / amend / append as
601 for (int i = 0; i < hiddenColumns.size(); i++)
603 int[] region = hiddenColumns.get(i);
605 if (end < region[0] - 1)
608 * insert discontiguous preceding range
610 hiddenColumns.add(i, new int[] { start, end });
614 if (end <= region[1])
617 * new range overlaps existing, or is contiguous preceding it - adjust
620 region[0] = Math.min(region[0], start);
624 if (start <= region[1] + 1)
627 * new range overlaps existing, or is contiguous following it - adjust
628 * start and end columns
630 region[0] = Math.min(region[0], start);
631 region[1] = Math.max(region[1], end);
634 * also update or remove any subsequent ranges
635 * that are overlapped
637 while (i < hiddenColumns.size() - 1)
639 int[] nextRegion = hiddenColumns.get(i + 1);
640 if (nextRegion[0] > end + 1)
643 * gap to next hidden range - no more to update
647 region[1] = Math.max(nextRegion[1], end);
648 hiddenColumns.remove(i + 1);
655 * remaining case is that the new range follows everything else
657 hiddenColumns.add(new int[] { start, end });
660 if (!wasAlreadyLocked)
662 LOCK.writeLock().unlock();
667 public boolean isVisible(int column)
671 LOCK.readLock().lock();
673 if (hiddenColumns != null)
675 for (int[] region : hiddenColumns)
677 if (column >= region[0] && column <= region[1])
687 LOCK.readLock().unlock();
691 private ArrayList<int[]> copyHiddenRegionsToArrayList(int startIndex)
694 if (hiddenColumns != null)
696 size = hiddenColumns.size();
698 ArrayList<int[]> copy = new ArrayList<>(size);
700 for (int i = startIndex, j = size; i < j; i++)
704 rh = hiddenColumns.get(i);
707 cp = new int[rh.length];
708 System.arraycopy(rh, 0, cp, 0, rh.length);
717 * Returns a copy of the vector of hidden regions, as an ArrayList. Before
718 * using this method please consider if you really need access to the hidden
719 * regions - a new (or existing!) method on HiddenColumns might be more
722 * @return hidden regions as an ArrayList of [start,end] pairs
724 public ArrayList<int[]> getHiddenColumnsCopy()
728 LOCK.readLock().lock();
729 return copyHiddenRegionsToArrayList(0);
732 LOCK.readLock().unlock();
737 * return all visible segments between the given start and end boundaries
740 * (first column inclusive from 0)
742 * (last column - not inclusive)
743 * @return List<int[]> {[i_start, i_end], ..} where intervals lie in
744 * start<=i_start<=i_end<end
746 public List<int[]> getVisibleContigs(int start, int end)
750 LOCK.readLock().lock();
751 List<int[]> vcontigs = new ArrayList<>();
752 if (hiddenColumns != null && hiddenColumns.size() > 0)
758 for (int[] region : hiddenColumns)
760 hideStart = region[0];
764 if (hideEnd < vstart)
768 if (hideStart > vstart)
770 int[] contig = new int[] { vstart, hideStart - 1 };
771 vcontigs.add(contig);
773 vstart = hideEnd + 1;
775 // exit if we're past the end
784 int[] contig = new int[] { vstart, end - 1 };
785 vcontigs.add(contig);
790 int[] contig = new int[] { start, end - 1 };
791 vcontigs.add(contig);
796 LOCK.readLock().unlock();
800 public String[] getVisibleSequenceStrings(int start, int end,
805 LOCK.readLock().lock();
806 int iSize = seqs.length;
807 String[] selections = new String[iSize];
808 if (hiddenColumns != null && hiddenColumns.size() > 0)
810 for (int i = 0; i < iSize; i++)
812 StringBuffer visibleSeq = new StringBuffer();
814 int blockStart = start;
819 for (int[] region : hiddenColumns)
821 hideStart = region[0];
824 if (hideStart < start)
829 blockStart = Math.min(blockStart, hideEnd + 1);
830 blockEnd = Math.min(blockEnd, hideStart);
832 if (blockStart > blockEnd)
837 visibleSeq.append(seqs[i].getSequence(blockStart, blockEnd));
839 blockStart = hideEnd + 1;
843 if (end > blockStart)
845 visibleSeq.append(seqs[i].getSequence(blockStart, end));
848 selections[i] = visibleSeq.toString();
853 for (int i = 0; i < iSize; i++)
855 selections[i] = seqs[i].getSequenceAsString(start, end);
862 LOCK.readLock().unlock();
867 * Locate the first and last position visible for this sequence. if seq isn't
868 * visible then return the position of the left and right of the hidden
869 * boundary region, and the corresponding alignment column indices for the
870 * extent of the sequence
873 * @return int[] { visible start, visible end, first seqpos, last seqpos,
874 * alignment index for seq start, alignment index for seq end }
876 public int[] locateVisibleBoundsOfSequence(SequenceI seq)
880 LOCK.readLock().lock();
881 int fpos = seq.getStart();
882 int lpos = seq.getEnd();
885 if (hiddenColumns == null || hiddenColumns.size() == 0)
887 int ifpos = seq.findIndex(fpos) - 1;
888 int ilpos = seq.findIndex(lpos) - 1;
889 return new int[] { ifpos, ifpos, ilpos };
892 // Simply walk along the sequence whilst watching for hidden column
894 List<int[]> regions = getHiddenRegions();
897 int hideStart = seq.getLength();
903 boolean foundStart = false;
904 for (int p = 0, pLen = seq.getLength(); spos <= seq.getEnd()
907 if (!Comparison.isGap(seq.getCharAt(p)))
909 // keep track of first/last column
910 // containing sequence data regardless of visibility
916 // update hidden region start/end
917 while (hideEnd < p && rcount < regions.size())
919 int[] region = regions.get(rcount++);
921 visNext += region[0] - visPrev;
922 hideStart = region[0];
927 hideStart = seq.getLength();
929 // update visible boundary for sequence
940 // look for next sequence position
946 return new int[] { findColumnPosition(start), firstP, lastP };
948 // otherwise, sequence was completely hidden
949 return new int[] { visPrev, firstP, lastP };
952 LOCK.readLock().unlock();
957 * delete any columns in alignmentAnnotation that are hidden (including
958 * sequence associated annotation).
960 * @param alignmentAnnotation
962 public void makeVisibleAnnotation(AlignmentAnnotation alignmentAnnotation)
964 makeVisibleAnnotation(-1, -1, alignmentAnnotation);
968 * delete any columns in alignmentAnnotation that are hidden (including
969 * sequence associated annotation).
972 * remove any annotation to the right of this column
974 * remove any annotation to the left of this column
975 * @param alignmentAnnotation
976 * the annotation to operate on
978 public void makeVisibleAnnotation(int start, int end,
979 AlignmentAnnotation alignmentAnnotation)
983 LOCK.readLock().lock();
984 if (alignmentAnnotation.annotations == null)
988 if (start == end && end == -1)
991 end = alignmentAnnotation.annotations.length;
993 if (hiddenColumns != null && hiddenColumns.size() > 0)
995 // then mangle the alignmentAnnotation annotation array
996 Vector<Annotation[]> annels = new Vector<>();
997 Annotation[] els = null;
998 int blockStart = start;
1004 for (int[] region : hiddenColumns)
1006 hideStart = region[0];
1007 hideEnd = region[1];
1009 if (hideStart < start)
1014 blockStart = Math.min(blockStart, hideEnd + 1);
1015 blockEnd = Math.min(blockEnd, hideStart);
1017 if (blockStart > blockEnd)
1022 els = new Annotation[blockEnd - blockStart];
1023 annels.addElement(els);
1024 System.arraycopy(alignmentAnnotation.annotations, blockStart, els,
1027 blockStart = hideEnd + 1;
1031 if (end > blockStart)
1033 els = new Annotation[end - blockStart + 1];
1034 annels.addElement(els);
1036 + blockStart) <= alignmentAnnotation.annotations.length)
1038 // copy just the visible segment of the annotation row
1039 System.arraycopy(alignmentAnnotation.annotations, blockStart,
1040 els, 0, els.length);
1044 // copy to the end of the annotation row
1045 System.arraycopy(alignmentAnnotation.annotations, blockStart,
1047 (alignmentAnnotation.annotations.length - blockStart));
1056 alignmentAnnotation.annotations = new Annotation[w];
1059 for (Annotation[] chnk : annels)
1061 System.arraycopy(chnk, 0, alignmentAnnotation.annotations, w,
1068 alignmentAnnotation.restrict(start, end);
1072 LOCK.readLock().unlock();
1078 * @return true if there are columns hidden
1080 public boolean hasHiddenColumns()
1084 LOCK.readLock().lock();
1085 return hiddenColumns != null && hiddenColumns.size() > 0;
1088 LOCK.readLock().unlock();
1094 * @return true if there are more than one set of columns hidden
1096 public boolean hasManyHiddenColumns()
1100 LOCK.readLock().lock();
1101 return hiddenColumns != null && hiddenColumns.size() > 1;
1104 LOCK.readLock().unlock();
1109 * mark the columns corresponding to gap characters as hidden in the column
1114 public void hideInsertionsFor(SequenceI sr)
1118 LOCK.writeLock().lock();
1119 List<int[]> inserts = sr.getInsertions();
1120 for (int[] r : inserts)
1122 hideColumns(r[0], r[1]);
1126 LOCK.writeLock().unlock();
1131 * Unhides, and adds to the selection list, all hidden columns
1133 public void revealAllHiddenColumns(ColumnSelection sel)
1137 LOCK.writeLock().lock();
1138 if (hiddenColumns != null)
1140 for (int i = 0; i < hiddenColumns.size(); i++)
1142 int[] region = hiddenColumns.get(i);
1143 for (int j = region[0]; j < region[1] + 1; j++)
1150 hiddenColumns = null;
1153 LOCK.writeLock().unlock();
1158 * Reveals, and marks as selected, the hidden column range with the given
1163 public void revealHiddenColumns(int start, ColumnSelection sel)
1167 LOCK.writeLock().lock();
1168 for (int i = 0; i < hiddenColumns.size(); i++)
1170 int[] region = hiddenColumns.get(i);
1171 if (start == region[0])
1173 for (int j = region[0]; j < region[1] + 1; j++)
1178 hiddenColumns.remove(region);
1182 if (hiddenColumns.size() == 0)
1184 hiddenColumns = null;
1188 LOCK.writeLock().unlock();
1193 * Add gaps into the sequences aligned to profileseq under the given
1198 * - alignment to have gaps inserted into it
1200 * - alignment view where sequence corresponding to profileseq is
1202 * @return new HiddenColumns for new alignment view, with insertions into
1203 * profileseq marked as hidden.
1205 public static HiddenColumns propagateInsertions(SequenceI profileseq,
1206 AlignmentI al, AlignmentView input)
1210 char gc = al.getGapCharacter();
1211 Object[] alandhidden = input.getAlignmentAndHiddenColumns(gc);
1212 HiddenColumns nview = (HiddenColumns) alandhidden[1];
1213 SequenceI origseq = ((SequenceI[]) alandhidden[0])[profsqpos];
1214 nview.propagateInsertions(profileseq, al, origseq);
1221 * - sequence in al which corresponds to origseq
1223 * - alignment which is to have gaps inserted into it
1225 * - sequence corresponding to profileseq which defines gap map for
1228 private void propagateInsertions(SequenceI profileseq, AlignmentI al,
1231 char gc = al.getGapCharacter();
1233 // take the set of hidden columns, and the set of gaps in origseq,
1234 // and remove all the hidden gaps from hiddenColumns
1236 // first get the gaps as a Bitset
1237 BitSet gaps = origseq.gapBitset();
1239 // now calculate hidden ^ not(gap)
1240 BitSet hidden = new BitSet();
1241 markHiddenRegions(hidden);
1242 hidden.andNot(gaps);
1243 hiddenColumns = null;
1244 this.hideMarkedBits(hidden);
1246 // for each sequence in the alignment, except the profile sequence,
1247 // insert gaps corresponding to each hidden region
1248 // but where each hidden column region is shifted backwards by the number of
1249 // preceding visible gaps
1250 // update hidden columns at the same time
1251 Iterator<int[]> regions = iterator();
1252 ArrayList<int[]> newhidden = new ArrayList<>();
1254 int numGapsBefore = 0;
1255 int gapPosition = 0;
1256 while (regions.hasNext())
1258 // get region coordinates accounting for gaps
1259 // we can rely on gaps not being *in* hidden regions because we already
1261 int[] region = regions.next();
1262 while (gapPosition < region[0])
1265 if (gaps.get(gapPosition))
1271 int left = region[0] - numGapsBefore;
1272 int right = region[1] - numGapsBefore;
1273 newhidden.add(new int[] { left, right });
1275 // make a string with number of gaps = length of hidden region
1276 StringBuffer sb = new StringBuffer();
1277 for (int s = 0; s < right - left + 1; s++)
1281 padGaps(sb, left, profileseq, al);
1284 hiddenColumns = newhidden;
1288 * Pad gaps in all sequences in alignment except profileseq
1291 * gap string to insert
1293 * position to insert at
1295 * sequence not to pad
1297 * alignment to pad sequences in
1299 private void padGaps(StringBuffer sb, int pos, SequenceI profileseq,
1302 // loop over the sequences and pad with gaps where required
1303 for (int s = 0, ns = al.getHeight(); s < ns; s++)
1305 SequenceI sqobj = al.getSequenceAt(s);
1306 if (sqobj != profileseq)
1308 String sq = al.getSequenceAt(s).getSequenceAsString();
1309 if (sq.length() <= pos)
1312 int diff = pos - sq.length() - 1;
1317 while ((diff = pos - sq.length() - 1) > 0)
1319 if (diff >= sb.length())
1321 sq += sb.toString();
1325 char[] buf = new char[diff];
1326 sb.getChars(0, diff, buf, 0);
1327 sq += buf.toString();
1331 sq += sb.toString();
1335 al.getSequenceAt(s).setSequence(
1336 sq.substring(0, pos) + sb.toString() + sq.substring(pos));
1343 * Returns a hashCode built from hidden column ranges
1346 public int hashCode()
1350 LOCK.readLock().lock();
1352 if (hiddenColumns != null)
1354 for (int[] hidden : hiddenColumns)
1356 hashCode = HASH_MULTIPLIER * hashCode + hidden[0];
1357 hashCode = HASH_MULTIPLIER * hashCode + hidden[1];
1363 LOCK.readLock().unlock();
1368 * Hide columns corresponding to the marked bits
1371 * - columns map to bits starting from zero
1373 public void hideMarkedBits(BitSet inserts)
1377 LOCK.writeLock().lock();
1378 for (int firstSet = inserts
1379 .nextSetBit(0), lastSet = 0; firstSet >= 0; firstSet = inserts
1380 .nextSetBit(lastSet))
1382 lastSet = inserts.nextClearBit(firstSet);
1383 hideColumns(firstSet, lastSet - 1);
1387 LOCK.writeLock().unlock();
1394 * BitSet where hidden columns will be marked
1396 public void markHiddenRegions(BitSet inserts)
1400 LOCK.readLock().lock();
1401 if (hiddenColumns == null)
1405 for (int[] range : hiddenColumns)
1407 inserts.set(range[0], range[1] + 1);
1411 LOCK.readLock().unlock();
1416 * Calculate the visible start and end index of an alignment.
1419 * full alignment width
1420 * @return integer array where: int[0] = startIndex, and int[1] = endIndex
1422 public int[] getVisibleStartAndEndIndex(int width)
1426 LOCK.readLock().lock();
1427 int[] alignmentStartEnd = new int[] { 0, width - 1 };
1428 int startPos = alignmentStartEnd[0];
1429 int endPos = alignmentStartEnd[1];
1431 int[] lowestRange = new int[] { -1, -1 };
1432 int[] higestRange = new int[] { -1, -1 };
1434 if (hiddenColumns == null)
1436 return new int[] { startPos, endPos };
1439 for (int[] hiddenCol : hiddenColumns)
1441 lowestRange = (hiddenCol[0] <= startPos) ? hiddenCol : lowestRange;
1442 higestRange = (hiddenCol[1] >= endPos) ? hiddenCol : higestRange;
1445 if (lowestRange[0] == -1 && lowestRange[1] == -1)
1447 startPos = alignmentStartEnd[0];
1451 startPos = lowestRange[1] + 1;
1454 if (higestRange[0] == -1 && higestRange[1] == -1)
1456 endPos = alignmentStartEnd[1];
1460 endPos = higestRange[0] - 1;
1462 return new int[] { startPos, endPos };
1465 LOCK.readLock().unlock();
1471 * Finds the hidden region (if any) which starts or ends at res
1474 * visible residue position, unadjusted for hidden columns
1475 * @return region as [start,end] or null if no matching region is found
1477 public int[] getRegionWithEdgeAtRes(int res)
1481 LOCK.readLock().lock();
1482 int adjres = adjustForHiddenColumns(res);
1484 int[] reveal = null;
1485 if (hiddenColumns != null)
1487 for (int[] region : hiddenColumns)
1489 if (adjres + 1 == region[0] || adjres - 1 == region[1])
1499 LOCK.readLock().unlock();
1503 public Iterator<int[]> iterator()
1505 if (hiddenColumns != null)
1507 int last = hiddenColumns.get(hiddenColumns.size() - 1)[1];
1508 return new BoundedHiddenColsIterator(0, last, true);
1512 return new BoundedHiddenColsIterator(0, 0, true);
1516 public Iterator<int[]> getBoundedIterator(int start, int end,
1519 return new BoundedHiddenColsIterator(start, end, useCopy);
1522 public Iterator<Integer> getBoundedStartIterator(int start, int end,
1525 return new BoundedStartRegionIterator(start, end, useCopy);
1528 public Iterator<int[]> getBoundedVisRegionIterator(int start, int end)
1530 return new BoundedVisRegionIterator(start, end, true);
1534 * An iterator which iterates over hidden column regions in a range.
1541 class BoundedHiddenColsIterator implements Iterator<int[]>
1544 private int start; // start position to iterate from
1546 private int end; // end position to iterate to
1548 // current index in hiddenColumns
1549 private int currentPosition = 0;
1551 // current column in hiddenColumns
1552 private int[] currentRegion;
1554 // whether to make a local copy of hiddenColumns
1555 private final boolean useCopy;
1557 // local copy or reference to hiddenColumns
1558 private List<int[]> localHidden;
1561 * Construct an iterator over hiddenColums bounded at
1562 * [lowerBound,upperBound]
1565 * lower bound to iterate from
1567 * upper bound to iterate to
1568 * @param useCopyCols
1569 * whether to make a local copy of hiddenColumns for iteration (set
1570 * to true if calling from outwith the HiddenColumns class)
1572 BoundedHiddenColsIterator(int lowerBound, int upperBound,
1573 boolean useCopyCols)
1577 useCopy = useCopyCols;
1583 // assume that if useCopy is false the calling code has locked
1585 LOCK.readLock().lock();
1588 if (hiddenColumns != null)
1590 localHidden = new ArrayList<>();
1592 // iterate until a region overlaps with [start,end]
1594 while ((i < hiddenColumns.size())
1595 && (hiddenColumns.get(i)[1] < start))
1600 // iterate from start to end, adding each hidden region. Positions are
1601 // absolute, and all regions which *overlap* [start,end] are added.
1602 while (i < hiddenColumns.size()
1603 && (hiddenColumns.get(i)[0] <= end))
1607 rh = hiddenColumns.get(i);
1610 cp = new int[rh.length];
1611 System.arraycopy(rh, 0, cp, 0, rh.length);
1612 localHidden.add(cp);
1622 LOCK.readLock().unlock();
1628 public boolean hasNext()
1630 return (localHidden != null)
1631 && (currentPosition < localHidden.size());
1637 currentRegion = localHidden.get(currentPosition);
1639 return currentRegion;
1643 class BoundedStartRegionIterator implements Iterator<Integer>
1646 private int start; // start position to iterate from
1648 private int end; // end position to iterate to
1650 // current index in hiddenColumns
1651 private int currentPosition = 0;
1653 // local copy or reference to hiddenColumns
1654 private List<Integer> positions = null;
1657 * Construct an iterator over hiddenColums bounded at
1658 * [lowerBound,upperBound]
1661 * lower bound to iterate from
1663 * upper bound to iterate to
1664 * @param useCopyCols
1665 * whether to make a local copy of hiddenColumns for iteration (set
1666 * to true if calling from outwith the HiddenColumns class)
1668 BoundedStartRegionIterator(int lowerBound, int upperBound,
1678 // assume that if useCopy is false the calling code has locked
1680 LOCK.readLock().lock();
1683 if (hiddenColumns != null)
1685 positions = new ArrayList<>(hiddenColumns.size());
1687 // navigate to start, keeping count of hidden columns
1689 int hiddenSoFar = 0;
1690 while ((i < hiddenColumns.size())
1691 && (hiddenColumns.get(i)[0] < start + hiddenSoFar))
1693 int[] region = hiddenColumns.get(i);
1694 hiddenSoFar += region[1] - region[0] + 1;
1698 // iterate from start to end, adding start positions of each
1699 // hidden region. Positions are visible columns count, not absolute
1700 while (i < hiddenColumns.size()
1701 && (hiddenColumns.get(i)[0] <= end + hiddenSoFar))
1703 int[] region = hiddenColumns.get(i);
1704 positions.add(region[0] - hiddenSoFar);
1705 hiddenSoFar += region[1] - region[0] + 1;
1711 positions = new ArrayList<>();
1717 LOCK.readLock().unlock();
1723 public boolean hasNext()
1725 return (currentPosition < positions.size());
1729 public Integer next()
1731 int result = positions.get(currentPosition);
1737 class BoundedVisRegionIterator implements Iterator<int[]>
1739 private int start; // start position to iterate from
1741 private int end; // end position to iterate to
1743 // current region in visColumns
1744 private int[] currentRegion;
1746 // current index in visColumns
1747 private int currentPosition = 0;
1749 private List<int[]> vcontigs = null;
1752 * Construct an iterator over visibleColumn regions bounded at
1753 * [lowerBound,upperBound]
1756 * lower bound to iterate from
1758 * upper bound to iterate to
1759 * @param useCopyCols
1760 * whether to make a local copy for iteration (set to true if
1761 * calling from outwith the HiddenColumns class)
1763 BoundedVisRegionIterator(int lowerBound, int upperBound,
1773 // assume that if useCopy is false the calling code has locked
1775 LOCK.readLock().lock();
1778 int visStart = start;
1780 if (hiddenColumns != null)
1782 vcontigs = new ArrayList<>(hiddenColumns.size() + 1);
1784 // navigate to start, keeping count of hidden columns
1786 int[] region = null;
1787 while ((i < hiddenColumns.size())
1788 && (hiddenColumns.get(i)[0] <= start))
1792 // if there was a hidden region before (i>=1), and it ended after
1794 // and before end, adjust visStart to be just after that region
1797 region = hiddenColumns.get(i - 1);
1798 if ((region[1] > start) && (region[1] < end))
1800 visStart = region[1] + 1;
1802 else if (region[1] >= end)
1804 // previous hidden region covers whole range [start,end]
1805 // early exit - vcontigs is empty
1810 // iterate from start to end, adding start positions of each
1811 // hidden region. Positions are visible columns count, not absolute
1812 while (i < hiddenColumns.size()
1813 && (hiddenColumns.get(i)[0] < end))
1815 region = hiddenColumns.get(i);
1816 int[] prevVisibleRegion = new int[] { visStart, region[0] - 1 };
1817 vcontigs.add(prevVisibleRegion);
1818 visStart = region[1] + 1;
1821 // add on a final visible region if needed
1822 if (visStart <= end)
1824 int[] lastRegion = new int[] { visStart, end };
1825 vcontigs.add(lastRegion);
1830 vcontigs = new ArrayList<>();
1831 int[] lastRegion = new int[] { start, end };
1832 vcontigs.add(lastRegion);
1838 LOCK.readLock().unlock();
1844 public boolean hasNext()
1846 return (currentPosition < vcontigs.size());
1852 currentRegion = vcontigs.get(currentPosition);
1854 return currentRegion;