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 public boolean equals(Object obj)
194 LOCK.readLock().lock();
196 if (!(obj instanceof HiddenColumns))
200 HiddenColumns that = (HiddenColumns) obj;
203 * check hidden columns are either both null, or match
205 if (this.hiddenColumns == null)
207 return (that.hiddenColumns == null);
209 if (that.hiddenColumns == null
210 || that.hiddenColumns.size() != this.hiddenColumns.size())
215 for (int[] thisRange : hiddenColumns)
217 int[] thatRange = that.hiddenColumns.get(i++);
218 if (thisRange[0] != thatRange[0] || thisRange[1] != thatRange[1])
226 LOCK.readLock().unlock();
231 * Return absolute column index for a visible column index
234 * int column index in alignment view (count from zero)
235 * @return alignment column index for column
237 public int adjustForHiddenColumns(int column)
241 LOCK.readLock().lock();
243 if (hiddenColumns != null)
245 for (int i = 0; i < hiddenColumns.size(); i++)
247 int[] region = hiddenColumns.get(i);
248 if (result >= region[0])
250 result += region[1] - region[0] + 1;
257 LOCK.readLock().unlock();
262 * Use this method to find out where a column will appear in the visible
263 * alignment when hidden columns exist. If the column is not visible, then the
264 * left-most visible column will always be returned.
266 * @param hiddenColumn
267 * the column index in the full alignment including hidden columns
268 * @return the position of the column in the visible alignment
270 public int findColumnPosition(int hiddenColumn)
274 LOCK.readLock().lock();
275 int result = hiddenColumn;
276 if (hiddenColumns != null)
282 region = hiddenColumns.get(index++);
283 if (hiddenColumn > region[1])
285 result -= region[1] + 1 - region[0];
287 } while ((hiddenColumn > region[1])
288 && (index < hiddenColumns.size()));
290 if (hiddenColumn >= region[0] && hiddenColumn <= region[1])
292 // Here the hidden column is within a region, so
293 // we want to return the position of region[0]-1, adjusted for any
294 // earlier hidden columns.
295 // Calculate the difference between the actual hidden col position
296 // and region[0]-1, and then subtract from result to convert result
298 // the adjusted hiddenColumn value to the adjusted region[0]-1 value
300 // However, if the region begins at 0 we cannot return region[0]-1
308 return result - (hiddenColumn - region[0] + 1);
312 return result; // return the shifted position after removing hidden
316 LOCK.readLock().unlock();
321 * Find the visible column which is a given visible number of columns to the
322 * left of another visible column. i.e. for a startColumn x, the column which
323 * is distance 1 away will be column x-1.
325 * @param visibleDistance
326 * the number of visible columns to offset by
328 * the column to start from
329 * @return the position of the column in the visible alignment
331 public int subtractVisibleColumns(int visibleDistance, int startColumn)
336 LOCK.readLock().lock();
337 int distance = visibleDistance;
339 // in case startColumn is in a hidden region, move it to the left
340 int start = adjustForHiddenColumns(findColumnPosition(startColumn));
342 // get index of hidden region to left of start
343 int index = getHiddenIndexLeft(start);
346 // no hidden regions to left of startColumn
347 return start - distance;
350 // walk backwards through the alignment subtracting the counts of visible
351 // columns from distance
354 int nextstart = start;
356 while ((index > -1) && (distance - gap > 0))
358 // subtract the gap to right of region from distance
362 // calculate the next gap
363 region = hiddenColumns.get(index);
364 gap = start - region[1];
366 // set start to just to left of current region
367 nextstart = region[0] - 1;
371 if (distance - gap > 0)
373 // fell out of loop because there are no more hidden regions
375 return nextstart - distance;
377 return start - distance;
380 LOCK.readLock().unlock();
386 * Use this method to determine the set of hiddenRegion start positions
387 * between absolute position <start> and absolute position <end>
390 * absolute residue to start from
392 * absolute residue to end at
394 * @return list of column numbers in *visible* view where hidden regions start
396 public List<Integer> findHiddenRegionPositions(int start, int end)
400 LOCK.readLock().lock();
401 List<Integer> positions = null;
403 if (hiddenColumns != null)
405 positions = new ArrayList<>(hiddenColumns.size());
407 // navigate to start, keeping count of hidden columns
410 while ((i < hiddenColumns.size())
411 && (hiddenColumns.get(i)[0] < start))
413 int[] region = hiddenColumns.get(i);
414 hiddenSoFar += region[1] - region[0] + 1;
418 // iterate from start to end, adding start positions of each
419 // hidden region. Positions are visible columns count, not absolute
420 while (i < hiddenColumns.size()
421 && (hiddenColumns.get(i)[0] < end))
423 int[] region = hiddenColumns.get(i);
424 positions.add(region[0] - hiddenSoFar);
425 hiddenSoFar += region[1] - region[0] + 1;
431 positions = new ArrayList<>();
437 LOCK.readLock().unlock();
442 * This method returns the rightmost limit of a region of an alignment with
443 * hidden columns. In otherwords, the next hidden column.
448 public int getHiddenBoundaryRight(int alPos)
452 LOCK.readLock().lock();
453 if (hiddenColumns != null)
458 int[] region = hiddenColumns.get(index);
459 if (alPos < region[0])
465 } while (index < hiddenColumns.size());
471 LOCK.readLock().unlock();
477 * This method returns the leftmost limit of a region of an alignment with
478 * hidden columns. In otherwords, the previous hidden column.
483 public int getHiddenBoundaryLeft(int alPos)
487 LOCK.readLock().lock();
489 if (hiddenColumns != null)
491 int index = hiddenColumns.size() - 1;
494 int[] region = hiddenColumns.get(index);
495 if (alPos > region[1])
501 } while (index > -1);
507 LOCK.readLock().unlock();
512 * This method returns the index of the hidden region to the left of a column
513 * position. If the column is in a hidden region it returns the index of the
514 * region to the left. If there is no hidden region to the left it returns -1.
519 private int getHiddenIndexLeft(int pos)
524 LOCK.readLock().lock();
525 if (hiddenColumns != null)
527 int index = hiddenColumns.size() - 1;
530 int[] region = hiddenColumns.get(index);
537 } while (index > -1);
543 LOCK.readLock().unlock();
549 * Adds the specified column range to the hidden columns
554 public void hideColumns(int start, int end)
556 boolean wasAlreadyLocked = false;
559 // check if the write lock was already locked by this thread,
560 // as this method can be called internally in loops within HiddenColumns
561 if (!LOCK.isWriteLockedByCurrentThread())
563 LOCK.writeLock().lock();
567 wasAlreadyLocked = true;
570 if (hiddenColumns == null)
572 hiddenColumns = new ArrayList<>();
576 * traverse existing hidden ranges and insert / amend / append as
579 for (int i = 0; i < hiddenColumns.size(); i++)
581 int[] region = hiddenColumns.get(i);
583 if (end < region[0] - 1)
586 * insert discontiguous preceding range
588 hiddenColumns.add(i, new int[] { start, end });
592 if (end <= region[1])
595 * new range overlaps existing, or is contiguous preceding it - adjust
598 region[0] = Math.min(region[0], start);
602 if (start <= region[1] + 1)
605 * new range overlaps existing, or is contiguous following it - adjust
606 * start and end columns
608 region[0] = Math.min(region[0], start);
609 region[1] = Math.max(region[1], end);
612 * also update or remove any subsequent ranges
613 * that are overlapped
615 while (i < hiddenColumns.size() - 1)
617 int[] nextRegion = hiddenColumns.get(i + 1);
618 if (nextRegion[0] > end + 1)
621 * gap to next hidden range - no more to update
625 region[1] = Math.max(nextRegion[1], end);
626 hiddenColumns.remove(i + 1);
633 * remaining case is that the new range follows everything else
635 hiddenColumns.add(new int[] { start, end });
638 if (!wasAlreadyLocked)
640 LOCK.writeLock().unlock();
645 public boolean isVisible(int column)
649 LOCK.readLock().lock();
651 if (hiddenColumns != null)
653 for (int[] region : hiddenColumns)
655 if (column >= region[0] && column <= region[1])
665 LOCK.readLock().unlock();
669 private ArrayList<int[]> copyHiddenRegionsToArrayList(int startIndex)
672 if (hiddenColumns != null)
674 size = hiddenColumns.size();
676 ArrayList<int[]> copy = new ArrayList<>(size);
678 for (int i = startIndex, j = size; i < j; i++)
682 rh = hiddenColumns.get(i);
685 cp = new int[rh.length];
686 System.arraycopy(rh, 0, cp, 0, rh.length);
695 * Returns a copy of the vector of hidden regions, as an ArrayList. Before
696 * using this method please consider if you really need access to the hidden
697 * regions - a new (or existing!) method on HiddenColumns might be more
700 * @return hidden regions as an ArrayList of [start,end] pairs
702 public ArrayList<int[]> getHiddenColumnsCopy()
706 LOCK.readLock().lock();
707 return copyHiddenRegionsToArrayList(0);
710 LOCK.readLock().unlock();
715 * return all visible segments between the given start and end boundaries
718 * (first column inclusive from 0)
720 * (last column - not inclusive)
721 * @return int[] {i_start, i_end, ..} where intervals lie in
722 * start<=i_start<=i_end<end
724 public int[] getVisibleContigs(int start, int end)
728 LOCK.readLock().lock();
729 if (hiddenColumns != null && hiddenColumns.size() > 0)
731 // max limit on number of visible contigs
732 // so we can dimension array
733 int maxcontigs = end - start + 1;
734 if (maxcontigs > (hiddenColumns.size() + 1) * 2)
736 maxcontigs = (hiddenColumns.size() + 1) * 2;
738 int[] vcontigs = new int[maxcontigs];
744 for (int[] region : hiddenColumns)
746 hideStart = region[0];
750 if (hideEnd < vstart)
754 if (hideStart > vstart)
756 vcontigs[i * 2] = vstart;
757 vcontigs[i * 2 + 1] = hideStart - 1;
760 vstart = hideEnd + 1;
762 // exit if we're past the end
771 vcontigs[i * 2] = vstart;
772 vcontigs[i * 2 + 1] = end - 1;
776 // copy final array into array of correct size
777 int[] trimmmedContigs = new int[i * 2];
778 System.arraycopy(vcontigs, 0, trimmmedContigs, 0, i * 2);
780 return trimmmedContigs;
784 return new int[] { start, end - 1 };
788 LOCK.readLock().unlock();
792 public String[] getVisibleSequenceStrings(int start, int end,
797 LOCK.readLock().lock();
798 int iSize = seqs.length;
799 String[] selections = new String[iSize];
800 if (hiddenColumns != null && hiddenColumns.size() > 0)
802 for (int i = 0; i < iSize; i++)
804 StringBuffer visibleSeq = new StringBuffer();
806 int blockStart = start;
811 for (int[] region : hiddenColumns)
813 hideStart = region[0];
816 if (hideStart < start)
821 blockStart = Math.min(blockStart, hideEnd + 1);
822 blockEnd = Math.min(blockEnd, hideStart);
824 if (blockStart > blockEnd)
829 visibleSeq.append(seqs[i].getSequence(blockStart, blockEnd));
831 blockStart = hideEnd + 1;
835 if (end > blockStart)
837 visibleSeq.append(seqs[i].getSequence(blockStart, end));
840 selections[i] = visibleSeq.toString();
845 for (int i = 0; i < iSize; i++)
847 selections[i] = seqs[i].getSequenceAsString(start, end);
854 LOCK.readLock().unlock();
859 * Locate the first and last position visible for this sequence. if seq isn't
860 * visible then return the position of the left and right of the hidden
861 * boundary region, and the corresponding alignment column indices for the
862 * extent of the sequence
865 * @return int[] { visible start, visible end, first seqpos, last seqpos,
866 * alignment index for seq start, alignment index for seq end }
868 public int[] locateVisibleBoundsOfSequence(SequenceI seq)
872 LOCK.readLock().lock();
873 int fpos = seq.getStart();
874 int lpos = seq.getEnd();
877 if (hiddenColumns == null || hiddenColumns.size() == 0)
879 int ifpos = seq.findIndex(fpos) - 1;
880 int ilpos = seq.findIndex(lpos) - 1;
881 return new int[] { ifpos, ifpos, ilpos };
884 // Simply walk along the sequence whilst watching for hidden column
886 List<int[]> regions = getHiddenRegions();
889 int hideStart = seq.getLength();
895 boolean foundStart = false;
896 for (int p = 0, pLen = seq.getLength(); spos <= seq.getEnd()
899 if (!Comparison.isGap(seq.getCharAt(p)))
901 // keep track of first/last column
902 // containing sequence data regardless of visibility
908 // update hidden region start/end
909 while (hideEnd < p && rcount < regions.size())
911 int[] region = regions.get(rcount++);
913 visNext += region[0] - visPrev;
914 hideStart = region[0];
919 hideStart = seq.getLength();
921 // update visible boundary for sequence
932 // look for next sequence position
938 return new int[] { findColumnPosition(start), firstP, lastP };
940 // otherwise, sequence was completely hidden
941 return new int[] { visPrev, firstP, lastP };
944 LOCK.readLock().unlock();
949 * delete any columns in alignmentAnnotation that are hidden (including
950 * sequence associated annotation).
952 * @param alignmentAnnotation
954 public void makeVisibleAnnotation(AlignmentAnnotation alignmentAnnotation)
956 makeVisibleAnnotation(-1, -1, alignmentAnnotation);
960 * delete any columns in alignmentAnnotation that are hidden (including
961 * sequence associated annotation).
964 * remove any annotation to the right of this column
966 * remove any annotation to the left of this column
967 * @param alignmentAnnotation
968 * the annotation to operate on
970 public void makeVisibleAnnotation(int start, int end,
971 AlignmentAnnotation alignmentAnnotation)
975 LOCK.readLock().lock();
976 if (alignmentAnnotation.annotations == null)
980 if (start == end && end == -1)
983 end = alignmentAnnotation.annotations.length;
985 if (hiddenColumns != null && hiddenColumns.size() > 0)
987 // then mangle the alignmentAnnotation annotation array
988 Vector<Annotation[]> annels = new Vector<>();
989 Annotation[] els = null;
990 int blockStart = start;
996 for (int[] region : hiddenColumns)
998 hideStart = region[0];
1001 if (hideStart < start)
1006 blockStart = Math.min(blockStart, hideEnd + 1);
1007 blockEnd = Math.min(blockEnd, hideStart);
1009 if (blockStart > blockEnd)
1014 els = new Annotation[blockEnd - blockStart];
1015 annels.addElement(els);
1016 System.arraycopy(alignmentAnnotation.annotations, blockStart, els,
1019 blockStart = hideEnd + 1;
1023 if (end > blockStart)
1025 els = new Annotation[end - blockStart + 1];
1026 annels.addElement(els);
1028 + blockStart) <= alignmentAnnotation.annotations.length)
1030 // copy just the visible segment of the annotation row
1031 System.arraycopy(alignmentAnnotation.annotations, blockStart,
1032 els, 0, els.length);
1036 // copy to the end of the annotation row
1037 System.arraycopy(alignmentAnnotation.annotations, blockStart,
1039 (alignmentAnnotation.annotations.length - blockStart));
1048 alignmentAnnotation.annotations = new Annotation[w];
1051 for (Annotation[] chnk : annels)
1053 System.arraycopy(chnk, 0, alignmentAnnotation.annotations, w,
1060 alignmentAnnotation.restrict(start, end);
1064 LOCK.readLock().unlock();
1070 * @return true if there are columns hidden
1072 public boolean hasHiddenColumns()
1076 LOCK.readLock().lock();
1077 return hiddenColumns != null && hiddenColumns.size() > 0;
1080 LOCK.readLock().unlock();
1086 * @return true if there are more than one set of columns hidden
1088 public boolean hasManyHiddenColumns()
1092 LOCK.readLock().lock();
1093 return hiddenColumns != null && hiddenColumns.size() > 1;
1096 LOCK.readLock().unlock();
1101 * mark the columns corresponding to gap characters as hidden in the column
1106 public void hideInsertionsFor(SequenceI sr)
1110 LOCK.writeLock().lock();
1111 List<int[]> inserts = sr.getInsertions();
1112 for (int[] r : inserts)
1114 hideColumns(r[0], r[1]);
1118 LOCK.writeLock().unlock();
1123 * Unhides, and adds to the selection list, all hidden columns
1125 public void revealAllHiddenColumns(ColumnSelection sel)
1129 LOCK.writeLock().lock();
1130 if (hiddenColumns != null)
1132 for (int i = 0; i < hiddenColumns.size(); i++)
1134 int[] region = hiddenColumns.get(i);
1135 for (int j = region[0]; j < region[1] + 1; j++)
1142 hiddenColumns = null;
1145 LOCK.writeLock().unlock();
1150 * Reveals, and marks as selected, the hidden column range with the given
1155 public void revealHiddenColumns(int start, ColumnSelection sel)
1159 LOCK.writeLock().lock();
1160 for (int i = 0; i < hiddenColumns.size(); i++)
1162 int[] region = hiddenColumns.get(i);
1163 if (start == region[0])
1165 for (int j = region[0]; j < region[1] + 1; j++)
1170 hiddenColumns.remove(region);
1174 if (hiddenColumns.size() == 0)
1176 hiddenColumns = null;
1180 LOCK.writeLock().unlock();
1185 * Add gaps into the sequences aligned to profileseq under the given
1190 * - alignment to have gaps inserted into it
1192 * - alignment view where sequence corresponding to profileseq is
1194 * @return new HiddenColumns for new alignment view, with insertions into
1195 * profileseq marked as hidden.
1197 public static HiddenColumns propagateInsertions(SequenceI profileseq,
1198 AlignmentI al, AlignmentView input)
1202 char gc = al.getGapCharacter();
1203 Object[] alandhidden = input.getAlignmentAndHiddenColumns(gc);
1204 HiddenColumns nview = (HiddenColumns) alandhidden[1];
1205 SequenceI origseq = ((SequenceI[]) alandhidden[0])[profsqpos];
1206 nview.propagateInsertions(profileseq, al, origseq);
1213 * - sequence in al which corresponds to origseq
1215 * - alignment which is to have gaps inserted into it
1217 * - sequence corresponding to profileseq which defines gap map for
1220 private void propagateInsertions(SequenceI profileseq, AlignmentI al,
1223 char gc = al.getGapCharacter();
1225 // take the set of hidden columns, and the set of gaps in origseq,
1226 // and remove all the hidden gaps from hiddenColumns
1228 // first get the gaps as a Bitset
1229 BitSet gaps = origseq.gapBitset();
1231 // now calculate hidden ^ not(gap)
1232 BitSet hidden = new BitSet();
1233 markHiddenRegions(hidden);
1234 hidden.andNot(gaps);
1235 hiddenColumns = null;
1236 this.hideMarkedBits(hidden);
1238 // for each sequence in the alignment, except the profile sequence,
1239 // insert gaps corresponding to each hidden region
1240 // but where each hidden column region is shifted backwards by the number of
1241 // preceding visible gaps
1242 // update hidden columns at the same time
1243 ArrayList<int[]> regions = getHiddenColumnsCopy();
1244 ArrayList<int[]> newhidden = new ArrayList<>();
1246 int numGapsBefore = 0;
1247 int gapPosition = 0;
1248 for (int[] region : regions)
1250 // get region coordinates accounting for gaps
1251 // we can rely on gaps not being *in* hidden regions because we already
1253 while (gapPosition < region[0])
1256 if (gaps.get(gapPosition))
1262 int left = region[0] - numGapsBefore;
1263 int right = region[1] - numGapsBefore;
1264 newhidden.add(new int[] { left, right });
1266 // make a string with number of gaps = length of hidden region
1267 StringBuffer sb = new StringBuffer();
1268 for (int s = 0; s < right - left + 1; s++)
1272 padGaps(sb, left, profileseq, al);
1275 hiddenColumns = newhidden;
1279 * Pad gaps in all sequences in alignment except profileseq
1282 * gap string to insert
1284 * position to insert at
1286 * sequence not to pad
1288 * alignment to pad sequences in
1290 private void padGaps(StringBuffer sb, int pos, SequenceI profileseq,
1293 // loop over the sequences and pad with gaps where required
1294 for (int s = 0, ns = al.getHeight(); s < ns; s++)
1296 SequenceI sqobj = al.getSequenceAt(s);
1297 if (sqobj != profileseq)
1299 String sq = al.getSequenceAt(s).getSequenceAsString();
1300 if (sq.length() <= pos)
1303 int diff = pos - sq.length() - 1;
1308 while ((diff = pos - sq.length() - 1) > 0)
1310 if (diff >= sb.length())
1312 sq += sb.toString();
1316 char[] buf = new char[diff];
1317 sb.getChars(0, diff, buf, 0);
1318 sq += buf.toString();
1322 sq += sb.toString();
1326 al.getSequenceAt(s).setSequence(
1327 sq.substring(0, pos) + sb.toString() + sq.substring(pos));
1334 * Returns a hashCode built from hidden column ranges
1337 public int hashCode()
1341 LOCK.readLock().lock();
1343 if (hiddenColumns != null)
1345 for (int[] hidden : hiddenColumns)
1347 hashCode = HASH_MULTIPLIER * hashCode + hidden[0];
1348 hashCode = HASH_MULTIPLIER * hashCode + hidden[1];
1354 LOCK.readLock().unlock();
1359 * Hide columns corresponding to the marked bits
1362 * - columns map to bits starting from zero
1364 public void hideMarkedBits(BitSet inserts)
1368 LOCK.writeLock().lock();
1369 for (int firstSet = inserts
1370 .nextSetBit(0), lastSet = 0; firstSet >= 0; firstSet = inserts
1371 .nextSetBit(lastSet))
1373 lastSet = inserts.nextClearBit(firstSet);
1374 hideColumns(firstSet, lastSet - 1);
1378 LOCK.writeLock().unlock();
1385 * BitSet where hidden columns will be marked
1387 public void markHiddenRegions(BitSet inserts)
1391 LOCK.readLock().lock();
1392 if (hiddenColumns == null)
1396 for (int[] range : hiddenColumns)
1398 inserts.set(range[0], range[1] + 1);
1402 LOCK.readLock().unlock();
1407 * Calculate the visible start and end index of an alignment.
1410 * full alignment width
1411 * @return integer array where: int[0] = startIndex, and int[1] = endIndex
1413 public int[] getVisibleStartAndEndIndex(int width)
1417 LOCK.readLock().lock();
1418 int[] alignmentStartEnd = new int[] { 0, width - 1 };
1419 int startPos = alignmentStartEnd[0];
1420 int endPos = alignmentStartEnd[1];
1422 int[] lowestRange = new int[] { -1, -1 };
1423 int[] higestRange = new int[] { -1, -1 };
1425 if (hiddenColumns == null)
1427 return new int[] { startPos, endPos };
1430 for (int[] hiddenCol : hiddenColumns)
1432 lowestRange = (hiddenCol[0] <= startPos) ? hiddenCol : lowestRange;
1433 higestRange = (hiddenCol[1] >= endPos) ? hiddenCol : higestRange;
1436 if (lowestRange[0] == -1 && lowestRange[1] == -1)
1438 startPos = alignmentStartEnd[0];
1442 startPos = lowestRange[1] + 1;
1445 if (higestRange[0] == -1 && higestRange[1] == -1)
1447 endPos = alignmentStartEnd[1];
1451 endPos = higestRange[0] - 1;
1453 return new int[] { startPos, endPos };
1456 LOCK.readLock().unlock();
1462 * Finds the hidden region (if any) which starts or ends at res
1465 * visible residue position, unadjusted for hidden columns
1466 * @return region as [start,end] or null if no matching region is found
1468 public int[] getRegionWithEdgeAtRes(int res)
1472 LOCK.readLock().lock();
1473 int adjres = adjustForHiddenColumns(res);
1475 int[] reveal = null;
1476 if (hiddenColumns != null)
1478 for (int[] region : hiddenColumns)
1480 if (adjres + 1 == region[0] || adjres - 1 == region[1])
1490 LOCK.readLock().unlock();
1494 public Iterator<int[]> iterator()
1496 if (hiddenColumns != null)
1498 return new BoundedHiddenColsIterator(0, hiddenColumns.size(), true);
1502 return new BoundedHiddenColsIterator(0, 0, true);
1506 public Iterator<int[]> getBoundedIterator(int start, int end,
1509 return new BoundedHiddenColsIterator(start, end, useCopy);
1512 public Iterator<Integer> getBoundedStartIterator(int start, int end,
1515 return new BoundedStartRegionIterator(start, end, useCopy);
1519 * An iterator which iterates over hidden column regions in a range.
1526 class BoundedHiddenColsIterator implements Iterator<int[]>
1529 private int start; // start position to iterate from
1531 private int end; // end position to iterate to
1533 // current index in hiddenColumns
1534 private int currentPosition = 0;
1536 // current column in hiddenColumns
1537 private int[] currentRegion;
1539 // whether to make a local copy of hiddenColumns
1540 private final boolean useCopy;
1542 // local copy or reference to hiddenColumns
1543 private List<int[]> localHidden;
1546 * Construct an iterator over hiddenColums bounded at
1547 * [lowerBound,upperBound]
1550 * lower bound to iterate from
1552 * upper bound to iterate to
1554 * Option.OVERLAP: regions which overlap [lowerBound,upperBound]
1555 * are included Option.START: regions which start in
1556 * [lowerBound,upperBound] are included
1557 * @param useAbsolutePos
1558 * have bounds and return values with reference to absolute indices
1559 * (if false, use indices for visible columns)
1560 * @param useCopyCols
1561 * whether to make a local copy of hiddenColumns for iteration (set
1562 * to true if calling from outwith the HiddenColumns class)
1564 BoundedHiddenColsIterator(int lowerBound, int upperBound,
1565 boolean useCopyCols)
1569 useCopy = useCopyCols;
1575 // assume that if useCopy is false the calling code has locked
1577 LOCK.readLock().lock();
1580 if (hiddenColumns != null)
1582 localHidden = new ArrayList<>();
1584 // iterate until a region overlaps with [start,end]
1586 while ((i < hiddenColumns.size())
1587 && (hiddenColumns.get(i)[1] < start))
1592 // iterate from start to end, adding each hidden region. Positions are
1593 // absolute, and all regions which *overlap* [start,end] are added.
1594 while (i < hiddenColumns.size()
1595 && (hiddenColumns.get(i)[0] <= end))
1599 rh = hiddenColumns.get(i);
1602 cp = new int[rh.length];
1603 System.arraycopy(rh, 0, cp, 0, rh.length);
1604 localHidden.add(cp);
1614 LOCK.readLock().unlock();
1620 public boolean hasNext()
1622 return (localHidden != null)
1623 && (currentPosition < localHidden.size());
1629 currentRegion = localHidden.get(currentPosition);
1631 return currentRegion;
1635 class BoundedStartRegionIterator implements Iterator<Integer>
1638 private int start; // start position to iterate from
1640 private int end; // end position to iterate to
1642 // current index in hiddenColumns
1643 private int currentPosition = 0;
1645 // local copy or reference to hiddenColumns
1646 private List<Integer> positions = null;
1649 * Construct an iterator over hiddenColums bounded at
1650 * [lowerBound,upperBound]
1653 * lower bound to iterate from
1655 * upper bound to iterate to
1656 * @param useCopyCols
1657 * whether to make a local copy of hiddenColumns for iteration (set
1658 * to true if calling from outwith the HiddenColumns class)
1660 BoundedStartRegionIterator(int lowerBound, int upperBound,
1670 // assume that if useCopy is false the calling code has locked
1672 LOCK.readLock().lock();
1675 if (hiddenColumns != null)
1677 positions = new ArrayList<>(hiddenColumns.size());
1679 // navigate to start, keeping count of hidden columns
1681 int hiddenSoFar = 0;
1682 while ((i < hiddenColumns.size())
1683 && (hiddenColumns.get(i)[0] < start + hiddenSoFar))
1685 int[] region = hiddenColumns.get(i);
1686 hiddenSoFar += region[1] - region[0] + 1;
1690 // iterate from start to end, adding start positions of each
1691 // hidden region. Positions are visible columns count, not absolute
1692 while (i < hiddenColumns.size()
1693 && (hiddenColumns.get(i)[0] <= end + hiddenSoFar))
1695 int[] region = hiddenColumns.get(i);
1696 positions.add(region[0] - hiddenSoFar);
1697 hiddenSoFar += region[1] - region[0] + 1;
1703 positions = new ArrayList<>();
1709 LOCK.readLock().unlock();
1715 public boolean hasNext()
1717 return (currentPosition < positions.size());
1721 public Integer next()
1723 int result = positions.get(currentPosition);