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 int[] {i_start, i_end, ..} where intervals lie in
744 * start<=i_start<=i_end<end
746 public int[] getVisibleContigs(int start, int end)
750 LOCK.readLock().lock();
751 if (hiddenColumns != null && hiddenColumns.size() > 0)
753 // max limit on number of visible contigs
754 // so we can dimension array
755 int maxcontigs = end - start + 1;
756 if (maxcontigs > (hiddenColumns.size() + 1) * 2)
758 maxcontigs = (hiddenColumns.size() + 1) * 2;
760 int[] vcontigs = new int[maxcontigs];
766 for (int[] region : hiddenColumns)
768 hideStart = region[0];
772 if (hideEnd < vstart)
776 if (hideStart > vstart)
778 vcontigs[i * 2] = vstart;
779 vcontigs[i * 2 + 1] = hideStart - 1;
782 vstart = hideEnd + 1;
784 // exit if we're past the end
793 vcontigs[i * 2] = vstart;
794 vcontigs[i * 2 + 1] = end - 1;
798 // copy final array into array of correct size
799 int[] trimmmedContigs = new int[i * 2];
800 System.arraycopy(vcontigs, 0, trimmmedContigs, 0, i * 2);
802 return trimmmedContigs;
806 return new int[] { start, end - 1 };
810 LOCK.readLock().unlock();
814 public String[] getVisibleSequenceStrings(int start, int end,
819 LOCK.readLock().lock();
820 int iSize = seqs.length;
821 String[] selections = new String[iSize];
822 if (hiddenColumns != null && hiddenColumns.size() > 0)
824 for (int i = 0; i < iSize; i++)
826 StringBuffer visibleSeq = new StringBuffer();
828 int blockStart = start;
833 for (int[] region : hiddenColumns)
835 hideStart = region[0];
838 if (hideStart < start)
843 blockStart = Math.min(blockStart, hideEnd + 1);
844 blockEnd = Math.min(blockEnd, hideStart);
846 if (blockStart > blockEnd)
851 visibleSeq.append(seqs[i].getSequence(blockStart, blockEnd));
853 blockStart = hideEnd + 1;
857 if (end > blockStart)
859 visibleSeq.append(seqs[i].getSequence(blockStart, end));
862 selections[i] = visibleSeq.toString();
867 for (int i = 0; i < iSize; i++)
869 selections[i] = seqs[i].getSequenceAsString(start, end);
876 LOCK.readLock().unlock();
881 * Locate the first and last position visible for this sequence. if seq isn't
882 * visible then return the position of the left and right of the hidden
883 * boundary region, and the corresponding alignment column indices for the
884 * extent of the sequence
887 * @return int[] { visible start, visible end, first seqpos, last seqpos,
888 * alignment index for seq start, alignment index for seq end }
890 public int[] locateVisibleBoundsOfSequence(SequenceI seq)
894 LOCK.readLock().lock();
895 int fpos = seq.getStart();
896 int lpos = seq.getEnd();
899 if (hiddenColumns == null || hiddenColumns.size() == 0)
901 int ifpos = seq.findIndex(fpos) - 1;
902 int ilpos = seq.findIndex(lpos) - 1;
903 return new int[] { ifpos, ifpos, ilpos };
906 // Simply walk along the sequence whilst watching for hidden column
908 List<int[]> regions = getHiddenRegions();
911 int hideStart = seq.getLength();
917 boolean foundStart = false;
918 for (int p = 0, pLen = seq.getLength(); spos <= seq.getEnd()
921 if (!Comparison.isGap(seq.getCharAt(p)))
923 // keep track of first/last column
924 // containing sequence data regardless of visibility
930 // update hidden region start/end
931 while (hideEnd < p && rcount < regions.size())
933 int[] region = regions.get(rcount++);
935 visNext += region[0] - visPrev;
936 hideStart = region[0];
941 hideStart = seq.getLength();
943 // update visible boundary for sequence
954 // look for next sequence position
960 return new int[] { findColumnPosition(start), firstP, lastP };
962 // otherwise, sequence was completely hidden
963 return new int[] { visPrev, firstP, lastP };
966 LOCK.readLock().unlock();
971 * delete any columns in alignmentAnnotation that are hidden (including
972 * sequence associated annotation).
974 * @param alignmentAnnotation
976 public void makeVisibleAnnotation(AlignmentAnnotation alignmentAnnotation)
978 makeVisibleAnnotation(-1, -1, alignmentAnnotation);
982 * delete any columns in alignmentAnnotation that are hidden (including
983 * sequence associated annotation).
986 * remove any annotation to the right of this column
988 * remove any annotation to the left of this column
989 * @param alignmentAnnotation
990 * the annotation to operate on
992 public void makeVisibleAnnotation(int start, int end,
993 AlignmentAnnotation alignmentAnnotation)
997 LOCK.readLock().lock();
998 if (alignmentAnnotation.annotations == null)
1002 if (start == end && end == -1)
1005 end = alignmentAnnotation.annotations.length;
1007 if (hiddenColumns != null && hiddenColumns.size() > 0)
1009 // then mangle the alignmentAnnotation annotation array
1010 Vector<Annotation[]> annels = new Vector<>();
1011 Annotation[] els = null;
1012 int blockStart = start;
1018 for (int[] region : hiddenColumns)
1020 hideStart = region[0];
1021 hideEnd = region[1];
1023 if (hideStart < start)
1028 blockStart = Math.min(blockStart, hideEnd + 1);
1029 blockEnd = Math.min(blockEnd, hideStart);
1031 if (blockStart > blockEnd)
1036 els = new Annotation[blockEnd - blockStart];
1037 annels.addElement(els);
1038 System.arraycopy(alignmentAnnotation.annotations, blockStart, els,
1041 blockStart = hideEnd + 1;
1045 if (end > blockStart)
1047 els = new Annotation[end - blockStart + 1];
1048 annels.addElement(els);
1050 + blockStart) <= alignmentAnnotation.annotations.length)
1052 // copy just the visible segment of the annotation row
1053 System.arraycopy(alignmentAnnotation.annotations, blockStart,
1054 els, 0, els.length);
1058 // copy to the end of the annotation row
1059 System.arraycopy(alignmentAnnotation.annotations, blockStart,
1061 (alignmentAnnotation.annotations.length - blockStart));
1070 alignmentAnnotation.annotations = new Annotation[w];
1073 for (Annotation[] chnk : annels)
1075 System.arraycopy(chnk, 0, alignmentAnnotation.annotations, w,
1082 alignmentAnnotation.restrict(start, end);
1086 LOCK.readLock().unlock();
1092 * @return true if there are columns hidden
1094 public boolean hasHiddenColumns()
1098 LOCK.readLock().lock();
1099 return hiddenColumns != null && hiddenColumns.size() > 0;
1102 LOCK.readLock().unlock();
1108 * @return true if there are more than one set of columns hidden
1110 public boolean hasManyHiddenColumns()
1114 LOCK.readLock().lock();
1115 return hiddenColumns != null && hiddenColumns.size() > 1;
1118 LOCK.readLock().unlock();
1123 * mark the columns corresponding to gap characters as hidden in the column
1128 public void hideInsertionsFor(SequenceI sr)
1132 LOCK.writeLock().lock();
1133 List<int[]> inserts = sr.getInsertions();
1134 for (int[] r : inserts)
1136 hideColumns(r[0], r[1]);
1140 LOCK.writeLock().unlock();
1145 * Unhides, and adds to the selection list, all hidden columns
1147 public void revealAllHiddenColumns(ColumnSelection sel)
1151 LOCK.writeLock().lock();
1152 if (hiddenColumns != null)
1154 for (int i = 0; i < hiddenColumns.size(); i++)
1156 int[] region = hiddenColumns.get(i);
1157 for (int j = region[0]; j < region[1] + 1; j++)
1164 hiddenColumns = null;
1167 LOCK.writeLock().unlock();
1172 * Reveals, and marks as selected, the hidden column range with the given
1177 public void revealHiddenColumns(int start, ColumnSelection sel)
1181 LOCK.writeLock().lock();
1182 for (int i = 0; i < hiddenColumns.size(); i++)
1184 int[] region = hiddenColumns.get(i);
1185 if (start == region[0])
1187 for (int j = region[0]; j < region[1] + 1; j++)
1192 hiddenColumns.remove(region);
1196 if (hiddenColumns.size() == 0)
1198 hiddenColumns = null;
1202 LOCK.writeLock().unlock();
1207 * Add gaps into the sequences aligned to profileseq under the given
1212 * - alignment to have gaps inserted into it
1214 * - alignment view where sequence corresponding to profileseq is
1216 * @return new HiddenColumns for new alignment view, with insertions into
1217 * profileseq marked as hidden.
1219 public static HiddenColumns propagateInsertions(SequenceI profileseq,
1220 AlignmentI al, AlignmentView input)
1224 char gc = al.getGapCharacter();
1225 Object[] alandhidden = input.getAlignmentAndHiddenColumns(gc);
1226 HiddenColumns nview = (HiddenColumns) alandhidden[1];
1227 SequenceI origseq = ((SequenceI[]) alandhidden[0])[profsqpos];
1228 nview.propagateInsertions(profileseq, al, origseq);
1235 * - sequence in al which corresponds to origseq
1237 * - alignment which is to have gaps inserted into it
1239 * - sequence corresponding to profileseq which defines gap map for
1242 private void propagateInsertions(SequenceI profileseq, AlignmentI al,
1245 char gc = al.getGapCharacter();
1247 // take the set of hidden columns, and the set of gaps in origseq,
1248 // and remove all the hidden gaps from hiddenColumns
1250 // first get the gaps as a Bitset
1251 BitSet gaps = origseq.gapBitset();
1253 // now calculate hidden ^ not(gap)
1254 BitSet hidden = new BitSet();
1255 markHiddenRegions(hidden);
1256 hidden.andNot(gaps);
1257 hiddenColumns = null;
1258 this.hideMarkedBits(hidden);
1260 // for each sequence in the alignment, except the profile sequence,
1261 // insert gaps corresponding to each hidden region
1262 // but where each hidden column region is shifted backwards by the number of
1263 // preceding visible gaps
1264 // update hidden columns at the same time
1265 Iterator<int[]> regions = iterator();
1266 ArrayList<int[]> newhidden = new ArrayList<>();
1268 int numGapsBefore = 0;
1269 int gapPosition = 0;
1270 while (regions.hasNext())
1272 // get region coordinates accounting for gaps
1273 // we can rely on gaps not being *in* hidden regions because we already
1275 int[] region = regions.next();
1276 while (gapPosition < region[0])
1279 if (gaps.get(gapPosition))
1285 int left = region[0] - numGapsBefore;
1286 int right = region[1] - numGapsBefore;
1287 newhidden.add(new int[] { left, right });
1289 // make a string with number of gaps = length of hidden region
1290 StringBuffer sb = new StringBuffer();
1291 for (int s = 0; s < right - left + 1; s++)
1295 padGaps(sb, left, profileseq, al);
1298 hiddenColumns = newhidden;
1302 * Pad gaps in all sequences in alignment except profileseq
1305 * gap string to insert
1307 * position to insert at
1309 * sequence not to pad
1311 * alignment to pad sequences in
1313 private void padGaps(StringBuffer sb, int pos, SequenceI profileseq,
1316 // loop over the sequences and pad with gaps where required
1317 for (int s = 0, ns = al.getHeight(); s < ns; s++)
1319 SequenceI sqobj = al.getSequenceAt(s);
1320 if (sqobj != profileseq)
1322 String sq = al.getSequenceAt(s).getSequenceAsString();
1323 if (sq.length() <= pos)
1326 int diff = pos - sq.length() - 1;
1331 while ((diff = pos - sq.length() - 1) > 0)
1333 if (diff >= sb.length())
1335 sq += sb.toString();
1339 char[] buf = new char[diff];
1340 sb.getChars(0, diff, buf, 0);
1341 sq += buf.toString();
1345 sq += sb.toString();
1349 al.getSequenceAt(s).setSequence(
1350 sq.substring(0, pos) + sb.toString() + sq.substring(pos));
1357 * Returns a hashCode built from hidden column ranges
1360 public int hashCode()
1364 LOCK.readLock().lock();
1366 if (hiddenColumns != null)
1368 for (int[] hidden : hiddenColumns)
1370 hashCode = HASH_MULTIPLIER * hashCode + hidden[0];
1371 hashCode = HASH_MULTIPLIER * hashCode + hidden[1];
1377 LOCK.readLock().unlock();
1382 * Hide columns corresponding to the marked bits
1385 * - columns map to bits starting from zero
1387 public void hideMarkedBits(BitSet inserts)
1391 LOCK.writeLock().lock();
1392 for (int firstSet = inserts
1393 .nextSetBit(0), lastSet = 0; firstSet >= 0; firstSet = inserts
1394 .nextSetBit(lastSet))
1396 lastSet = inserts.nextClearBit(firstSet);
1397 hideColumns(firstSet, lastSet - 1);
1401 LOCK.writeLock().unlock();
1408 * BitSet where hidden columns will be marked
1410 public void markHiddenRegions(BitSet inserts)
1414 LOCK.readLock().lock();
1415 if (hiddenColumns == null)
1419 for (int[] range : hiddenColumns)
1421 inserts.set(range[0], range[1] + 1);
1425 LOCK.readLock().unlock();
1430 * Calculate the visible start and end index of an alignment.
1433 * full alignment width
1434 * @return integer array where: int[0] = startIndex, and int[1] = endIndex
1436 public int[] getVisibleStartAndEndIndex(int width)
1440 LOCK.readLock().lock();
1441 int[] alignmentStartEnd = new int[] { 0, width - 1 };
1442 int startPos = alignmentStartEnd[0];
1443 int endPos = alignmentStartEnd[1];
1445 int[] lowestRange = new int[] { -1, -1 };
1446 int[] higestRange = new int[] { -1, -1 };
1448 if (hiddenColumns == null)
1450 return new int[] { startPos, endPos };
1453 for (int[] hiddenCol : hiddenColumns)
1455 lowestRange = (hiddenCol[0] <= startPos) ? hiddenCol : lowestRange;
1456 higestRange = (hiddenCol[1] >= endPos) ? hiddenCol : higestRange;
1459 if (lowestRange[0] == -1 && lowestRange[1] == -1)
1461 startPos = alignmentStartEnd[0];
1465 startPos = lowestRange[1] + 1;
1468 if (higestRange[0] == -1 && higestRange[1] == -1)
1470 endPos = alignmentStartEnd[1];
1474 endPos = higestRange[0] - 1;
1476 return new int[] { startPos, endPos };
1479 LOCK.readLock().unlock();
1485 * Finds the hidden region (if any) which starts or ends at res
1488 * visible residue position, unadjusted for hidden columns
1489 * @return region as [start,end] or null if no matching region is found
1491 public int[] getRegionWithEdgeAtRes(int res)
1495 LOCK.readLock().lock();
1496 int adjres = adjustForHiddenColumns(res);
1498 int[] reveal = null;
1499 if (hiddenColumns != null)
1501 for (int[] region : hiddenColumns)
1503 if (adjres + 1 == region[0] || adjres - 1 == region[1])
1513 LOCK.readLock().unlock();
1517 public Iterator<int[]> iterator()
1519 if (hiddenColumns != null)
1521 int last = hiddenColumns.get(hiddenColumns.size() - 1)[1];
1522 return new BoundedHiddenColsIterator(0, last, true);
1526 return new BoundedHiddenColsIterator(0, 0, true);
1530 public Iterator<int[]> getBoundedIterator(int start, int end,
1533 return new BoundedHiddenColsIterator(start, end, useCopy);
1536 public Iterator<Integer> getBoundedStartIterator(int start, int end,
1539 return new BoundedStartRegionIterator(start, end, useCopy);
1543 * An iterator which iterates over hidden column regions in a range.
1550 class BoundedHiddenColsIterator implements Iterator<int[]>
1553 private int start; // start position to iterate from
1555 private int end; // end position to iterate to
1557 // current index in hiddenColumns
1558 private int currentPosition = 0;
1560 // current column in hiddenColumns
1561 private int[] currentRegion;
1563 // whether to make a local copy of hiddenColumns
1564 private final boolean useCopy;
1566 // local copy or reference to hiddenColumns
1567 private List<int[]> localHidden;
1570 * Construct an iterator over hiddenColums bounded at
1571 * [lowerBound,upperBound]
1574 * lower bound to iterate from
1576 * upper bound to iterate to
1577 * @param useCopyCols
1578 * whether to make a local copy of hiddenColumns for iteration (set
1579 * to true if calling from outwith the HiddenColumns class)
1581 BoundedHiddenColsIterator(int lowerBound, int upperBound,
1582 boolean useCopyCols)
1586 useCopy = useCopyCols;
1592 // assume that if useCopy is false the calling code has locked
1594 LOCK.readLock().lock();
1597 if (hiddenColumns != null)
1599 localHidden = new ArrayList<>();
1601 // iterate until a region overlaps with [start,end]
1603 while ((i < hiddenColumns.size())
1604 && (hiddenColumns.get(i)[1] < start))
1609 // iterate from start to end, adding each hidden region. Positions are
1610 // absolute, and all regions which *overlap* [start,end] are added.
1611 while (i < hiddenColumns.size()
1612 && (hiddenColumns.get(i)[0] <= end))
1616 rh = hiddenColumns.get(i);
1619 cp = new int[rh.length];
1620 System.arraycopy(rh, 0, cp, 0, rh.length);
1621 localHidden.add(cp);
1631 LOCK.readLock().unlock();
1637 public boolean hasNext()
1639 return (localHidden != null)
1640 && (currentPosition < localHidden.size());
1646 currentRegion = localHidden.get(currentPosition);
1648 return currentRegion;
1652 class BoundedStartRegionIterator implements Iterator<Integer>
1655 private int start; // start position to iterate from
1657 private int end; // end position to iterate to
1659 // current index in hiddenColumns
1660 private int currentPosition = 0;
1662 // local copy or reference to hiddenColumns
1663 private List<Integer> positions = null;
1666 * Construct an iterator over hiddenColums bounded at
1667 * [lowerBound,upperBound]
1670 * lower bound to iterate from
1672 * upper bound to iterate to
1673 * @param useCopyCols
1674 * whether to make a local copy of hiddenColumns for iteration (set
1675 * to true if calling from outwith the HiddenColumns class)
1677 BoundedStartRegionIterator(int lowerBound, int upperBound,
1687 // assume that if useCopy is false the calling code has locked
1689 LOCK.readLock().lock();
1692 if (hiddenColumns != null)
1694 positions = new ArrayList<>(hiddenColumns.size());
1696 // navigate to start, keeping count of hidden columns
1698 int hiddenSoFar = 0;
1699 while ((i < hiddenColumns.size())
1700 && (hiddenColumns.get(i)[0] < start + hiddenSoFar))
1702 int[] region = hiddenColumns.get(i);
1703 hiddenSoFar += region[1] - region[0] + 1;
1707 // iterate from start to end, adding start positions of each
1708 // hidden region. Positions are visible columns count, not absolute
1709 while (i < hiddenColumns.size()
1710 && (hiddenColumns.get(i)[0] <= end + hiddenSoFar))
1712 int[] region = hiddenColumns.get(i);
1713 positions.add(region[0] - hiddenSoFar);
1714 hiddenSoFar += region[1] - region[0] + 1;
1720 positions = new ArrayList<>();
1726 LOCK.readLock().unlock();
1732 public boolean hasNext()
1734 return (currentPosition < positions.size());
1738 public Integer next()
1740 int result = positions.get(currentPosition);