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.List;
29 import java.util.Vector;
30 import java.util.concurrent.locks.ReentrantReadWriteLock;
32 public class HiddenColumns
34 private static final ReentrantReadWriteLock LOCK = new ReentrantReadWriteLock();
37 * list of hidden column [start, end] ranges; the list is maintained in
38 * ascending start column order
40 private ArrayList<int[]> hiddenColumns;
45 public HiddenColumns()
54 public HiddenColumns(HiddenColumns copy)
58 LOCK.writeLock().lock();
61 if (copy.hiddenColumns != null)
63 hiddenColumns = copy.copyHiddenRegionsToArrayList();
68 LOCK.writeLock().unlock();
73 * This method is used to return all the HiddenColumn regions and is intended
74 * to remain private. External callers which need a copy of the regions can
75 * call getHiddenColumnsCopyAsList.
77 * @return empty list or List of hidden column intervals
79 private List<int[]> getHiddenRegions()
81 return hiddenColumns == null ? Collections.<int[]> emptyList()
86 * Output regions data as a string. String is in the format:
87 * reg0[0]<between>reg0[1]<delimiter>reg1[0]<between>reg1[1] ... regn[1]
90 * string to delimit regions
91 * @param betweenstring
92 * to put between start and end region values
93 * @return regions formatted according to delimiter and between strings
95 public String regionsToString(String delimiter, String between)
99 LOCK.readLock().lock();
100 StringBuilder regionBuilder = new StringBuilder();
101 if (hiddenColumns != null)
103 for (int[] range : hiddenColumns)
105 regionBuilder.append(delimiter).append(range[0]).append(between)
109 regionBuilder.deleteCharAt(0);
111 return regionBuilder.toString();
114 LOCK.readLock().unlock();
119 * Find the number of hidden columns
121 * @return number of hidden columns
127 LOCK.readLock().lock();
129 if (hasHiddenColumns())
131 for (int[] range : hiddenColumns)
133 size += range[1] - range[0] + 1;
139 LOCK.readLock().unlock();
144 public boolean equals(Object obj)
148 LOCK.readLock().lock();
150 if (!(obj instanceof HiddenColumns))
154 HiddenColumns that = (HiddenColumns) obj;
157 * check hidden columns are either both null, or match
159 if (this.hiddenColumns == null)
161 return (that.hiddenColumns == null);
163 if (that.hiddenColumns == null
164 || that.hiddenColumns.size() != this.hiddenColumns.size())
169 for (int[] thisRange : hiddenColumns)
171 int[] thatRange = that.hiddenColumns.get(i++);
172 if (thisRange[0] != thatRange[0] || thisRange[1] != thatRange[1])
180 LOCK.readLock().unlock();
185 * Return absolute column index for a visible column index
188 * int column index in alignment view (count from zero)
189 * @return alignment column index for column
191 public int adjustForHiddenColumns(int column)
195 LOCK.readLock().lock();
197 if (hiddenColumns != null)
199 for (int i = 0; i < hiddenColumns.size(); i++)
201 int[] region = hiddenColumns.get(i);
202 if (result >= region[0])
204 result += region[1] - region[0] + 1;
211 LOCK.readLock().unlock();
216 * Use this method to find out where a column will appear in the visible
217 * alignment when hidden columns exist. If the column is not visible, then the
218 * left-most visible column will always be returned.
220 * @param hiddenColumn
221 * the column index in the full alignment including hidden columns
222 * @return the position of the column in the visible alignment
224 public int findColumnPosition(int hiddenColumn)
228 LOCK.readLock().lock();
229 int result = hiddenColumn;
230 if (hiddenColumns != null)
236 region = hiddenColumns.get(index++);
237 if (hiddenColumn > region[1])
239 result -= region[1] + 1 - region[0];
241 } while ((hiddenColumn > region[1])
242 && (index < hiddenColumns.size()));
244 if (hiddenColumn >= region[0] && hiddenColumn <= region[1])
246 // Here the hidden column is within a region, so
247 // we want to return the position of region[0]-1, adjusted for any
248 // earlier hidden columns.
249 // Calculate the difference between the actual hidden col position
250 // and region[0]-1, and then subtract from result to convert result
252 // the adjusted hiddenColumn value to the adjusted region[0]-1 value
254 // However, if the region begins at 0 we cannot return region[0]-1
262 return result - (hiddenColumn - region[0] + 1);
266 return result; // return the shifted position after removing hidden
270 LOCK.readLock().unlock();
275 * Find the visible column which is a given visible number of columns to the
276 * left of another visible column. i.e. for a startColumn x, the column which
277 * is distance 1 away will be column x-1.
279 * @param visibleDistance
280 * the number of visible columns to offset by
282 * the column to start from
283 * @return the position of the column in the visible alignment
285 public int subtractVisibleColumns(int visibleDistance, int startColumn)
290 LOCK.readLock().lock();
291 int distance = visibleDistance;
293 // in case startColumn is in a hidden region, move it to the left
294 int start = adjustForHiddenColumns(findColumnPosition(startColumn));
296 // get index of hidden region to left of start
297 int index = getHiddenIndexLeft(start);
300 // no hidden regions to left of startColumn
301 return start - distance;
304 // walk backwards through the alignment subtracting the counts of visible
305 // columns from distance
308 int nextstart = start;
310 while ((index > -1) && (distance - gap > 0))
312 // subtract the gap to right of region from distance
316 // calculate the next gap
317 region = hiddenColumns.get(index);
318 gap = start - region[1];
320 // set start to just to left of current region
321 nextstart = region[0] - 1;
325 if (distance - gap > 0)
327 // fell out of loop because there are no more hidden regions
329 return nextstart - distance;
331 return start - distance;
334 LOCK.readLock().unlock();
340 * Use this method to determine the set of hiddenRegion start positions
342 * @return list of column number in visible view where hidden regions start
344 public List<Integer> findHiddenRegionPositions()
348 LOCK.readLock().lock();
349 List<Integer> positions = null;
351 if (hiddenColumns != null)
353 positions = new ArrayList<>(hiddenColumns.size());
355 positions.add(hiddenColumns.get(0)[0]);
356 for (int i = 1; i < hiddenColumns.size(); ++i)
360 if (hiddenColumns != null)
366 int[] region = hiddenColumns.get(index);
367 gaps += region[1] + 1 - region[0];
368 result = region[1] + 1;
370 } while (index <= i);
374 positions.add(result);
379 positions = new ArrayList<>();
385 LOCK.readLock().unlock();
390 * This method returns the rightmost limit of a region of an alignment with
391 * hidden columns. In otherwords, the next hidden column.
396 public int getHiddenBoundaryRight(int alPos)
400 LOCK.readLock().lock();
401 if (hiddenColumns != null)
406 int[] region = hiddenColumns.get(index);
407 if (alPos < region[0])
413 } while (index < hiddenColumns.size());
419 LOCK.readLock().unlock();
425 * This method returns the leftmost limit of a region of an alignment with
426 * hidden columns. In otherwords, the previous hidden column.
431 public int getHiddenBoundaryLeft(int alPos)
435 LOCK.readLock().lock();
437 if (hiddenColumns != null)
439 int index = hiddenColumns.size() - 1;
442 int[] region = hiddenColumns.get(index);
443 if (alPos > region[1])
449 } while (index > -1);
455 LOCK.readLock().unlock();
460 * This method returns the index of the hidden region to the left of a column
461 * position. If the column is in a hidden region it returns the index of the
462 * region to the left. If there is no hidden region to the left it returns -1.
467 private int getHiddenIndexLeft(int pos)
472 LOCK.readLock().lock();
473 if (hiddenColumns != null)
475 int index = hiddenColumns.size() - 1;
478 int[] region = hiddenColumns.get(index);
485 } while (index > -1);
491 LOCK.readLock().unlock();
497 * Adds the specified column range to the hidden columns
502 public void hideColumns(int start, int end)
504 boolean wasAlreadyLocked = false;
507 // check if the write lock was already locked by this thread,
508 // as this method can be called internally in loops within HiddenColumns
509 if (!LOCK.isWriteLockedByCurrentThread())
511 LOCK.writeLock().lock();
515 wasAlreadyLocked = true;
518 if (hiddenColumns == null)
520 hiddenColumns = new ArrayList<>();
524 * traverse existing hidden ranges and insert / amend / append as
527 for (int i = 0; i < hiddenColumns.size(); i++)
529 int[] region = hiddenColumns.get(i);
531 if (end < region[0] - 1)
534 * insert discontiguous preceding range
536 hiddenColumns.add(i, new int[] { start, end });
540 if (end <= region[1])
543 * new range overlaps existing, or is contiguous preceding it - adjust
546 region[0] = Math.min(region[0], start);
550 if (start <= region[1] + 1)
553 * new range overlaps existing, or is contiguous following it - adjust
554 * start and end columns
556 region[0] = Math.min(region[0], start);
557 region[1] = Math.max(region[1], end);
560 * also update or remove any subsequent ranges
561 * that are overlapped
563 while (i < hiddenColumns.size() - 1)
565 int[] nextRegion = hiddenColumns.get(i + 1);
566 if (nextRegion[0] > end + 1)
569 * gap to next hidden range - no more to update
573 region[1] = Math.max(nextRegion[1], end);
574 hiddenColumns.remove(i + 1);
581 * remaining case is that the new range follows everything else
583 hiddenColumns.add(new int[] { start, end });
586 if (!wasAlreadyLocked)
588 LOCK.writeLock().unlock();
593 public boolean isVisible(int column)
597 LOCK.readLock().lock();
599 if (hiddenColumns != null)
601 for (int[] region : hiddenColumns)
603 if (column >= region[0] && column <= region[1])
613 LOCK.readLock().unlock();
617 private ArrayList<int[]> copyHiddenRegionsToArrayList()
620 if (hiddenColumns != null)
622 size = hiddenColumns.size();
624 ArrayList<int[]> copy = new ArrayList<>(size);
626 for (int i = 0, j = size; i < j; i++)
630 rh = hiddenColumns.get(i);
633 cp = new int[rh.length];
634 System.arraycopy(rh, 0, cp, 0, rh.length);
643 * Returns a copy of the vector of hidden regions, as an ArrayList. Before
644 * using this method please consider if you really need access to the hidden
645 * regions - a new (or existing!) method on HiddenColumns might be more
648 * @return hidden regions as an ArrayList of [start,end] pairs
650 public ArrayList<int[]> getHiddenColumnsCopy()
654 LOCK.readLock().lock();
655 return copyHiddenRegionsToArrayList();
658 LOCK.readLock().unlock();
663 * propagate shift in alignment columns to column selection
668 * shift in edit (+ve for removal, or -ve for inserts)
670 public List<int[]> compensateForEdit(int start, int change,
675 LOCK.writeLock().lock();
676 List<int[]> deletedHiddenColumns = null;
678 if (hiddenColumns != null)
680 deletedHiddenColumns = new ArrayList<>();
681 int hSize = hiddenColumns.size();
682 for (int i = 0; i < hSize; i++)
684 int[] region = hiddenColumns.get(i);
685 if (region[0] > start && start + change > region[1])
687 deletedHiddenColumns.add(region);
689 hiddenColumns.remove(i);
695 if (region[0] > start)
708 this.revealHiddenColumns(0, sel);
711 return deletedHiddenColumns;
714 LOCK.writeLock().unlock();
719 * propagate shift in alignment columns to column selection special version of
720 * compensateForEdit - allowing for edits within hidden regions
725 * shift in edit (+ve for removal, or -ve for inserts)
727 public void compensateForDelEdits(int start, int change)
731 LOCK.writeLock().lock();
732 if (hiddenColumns != null)
734 for (int i = 0; i < hiddenColumns.size(); i++)
736 int[] region = hiddenColumns.get(i);
737 if (region[0] >= start)
741 if (region[1] >= start)
745 if (region[1] < region[0])
747 hiddenColumns.remove(i--);
762 LOCK.writeLock().unlock();
767 * return all visible segments between the given start and end boundaries
770 * (first column inclusive from 0)
772 * (last column - not inclusive)
773 * @return int[] {i_start, i_end, ..} where intervals lie in
774 * start<=i_start<=i_end<end
776 public int[] getVisibleContigs(int start, int end)
780 LOCK.readLock().lock();
781 if (hiddenColumns != null && hiddenColumns.size() > 0)
783 List<int[]> visiblecontigs = new ArrayList<>();
784 List<int[]> regions = getHiddenRegions();
791 for (int j = 0; vstart < end && j < regions.size(); j++)
793 region = regions.get(j);
794 hideStart = region[0];
797 if (hideEnd < vstart)
801 if (hideStart > vstart)
803 visiblecontigs.add(new int[] { vstart, hideStart - 1 });
805 vstart = hideEnd + 1;
810 visiblecontigs.add(new int[] { vstart, end - 1 });
812 int[] vcontigs = new int[visiblecontigs.size() * 2];
813 for (int i = 0, j = visiblecontigs.size(); i < j; i++)
815 int[] vc = visiblecontigs.get(i);
816 visiblecontigs.set(i, null);
817 vcontigs[i * 2] = vc[0];
818 vcontigs[i * 2 + 1] = vc[1];
820 visiblecontigs.clear();
825 return new int[] { start, end - 1 };
829 LOCK.readLock().unlock();
833 public String[] getVisibleSequenceStrings(int start, int end,
838 LOCK.readLock().lock();
839 int iSize = seqs.length;
840 String[] selections = new String[iSize];
841 if (hiddenColumns != null && hiddenColumns.size() > 0)
843 for (int i = 0; i < iSize; i++)
845 StringBuffer visibleSeq = new StringBuffer();
846 List<int[]> regions = getHiddenRegions();
848 int blockStart = start;
854 for (int j = 0; j < regions.size(); j++)
856 region = regions.get(j);
857 hideStart = region[0];
860 if (hideStart < start)
865 blockStart = Math.min(blockStart, hideEnd + 1);
866 blockEnd = Math.min(blockEnd, hideStart);
868 if (blockStart > blockEnd)
873 visibleSeq.append(seqs[i].getSequence(blockStart, blockEnd));
875 blockStart = hideEnd + 1;
879 if (end > blockStart)
881 visibleSeq.append(seqs[i].getSequence(blockStart, end));
884 selections[i] = visibleSeq.toString();
889 for (int i = 0; i < iSize; i++)
891 selections[i] = seqs[i].getSequenceAsString(start, end);
898 LOCK.readLock().unlock();
903 * Locate the first and last position visible for this sequence. if seq isn't
904 * visible then return the position of the left and right of the hidden
905 * boundary region, and the corresponding alignment column indices for the
906 * extent of the sequence
909 * @return int[] { visible start, visible end, first seqpos, last seqpos,
910 * alignment index for seq start, alignment index for seq end }
912 public int[] locateVisibleBoundsOfSequence(SequenceI seq)
916 LOCK.readLock().lock();
917 int fpos = seq.getStart();
918 int lpos = seq.getEnd();
921 if (hiddenColumns == null || hiddenColumns.size() == 0)
923 int ifpos = seq.findIndex(fpos) - 1;
924 int ilpos = seq.findIndex(lpos) - 1;
925 return new int[] { ifpos, ifpos, ilpos };
928 // Simply walk along the sequence whilst watching for hidden column
930 List<int[]> regions = getHiddenRegions();
934 int hideStart = seq.getLength();
940 boolean foundStart = false;
941 for (int p = 0, pLen = seq.getLength(); spos <= seq.getEnd()
944 if (!Comparison.isGap(seq.getCharAt(p)))
946 // keep track of first/last column
947 // containing sequence data regardless of visibility
953 // update hidden region start/end
954 while (hideEnd < p && rcount < regions.size())
956 int[] region = regions.get(rcount++);
958 visNext += region[0] - visPrev;
959 hideStart = region[0];
964 hideStart = seq.getLength();
966 // update visible boundary for sequence
978 // look for next sequence position
984 return new int[] { findColumnPosition(start), firstP, lastP };
986 // otherwise, sequence was completely hidden
987 return new int[] { visPrev, firstP, lastP };
990 LOCK.readLock().unlock();
995 * delete any columns in alignmentAnnotation that are hidden (including
996 * sequence associated annotation).
998 * @param alignmentAnnotation
1000 public void makeVisibleAnnotation(AlignmentAnnotation alignmentAnnotation)
1002 makeVisibleAnnotation(-1, -1, alignmentAnnotation);
1006 * delete any columns in alignmentAnnotation that are hidden (including
1007 * sequence associated annotation).
1010 * remove any annotation to the right of this column
1012 * remove any annotation to the left of this column
1013 * @param alignmentAnnotation
1014 * the annotation to operate on
1016 public void makeVisibleAnnotation(int start, int end,
1017 AlignmentAnnotation alignmentAnnotation)
1021 LOCK.readLock().lock();
1022 if (alignmentAnnotation.annotations == null)
1026 if (start == end && end == -1)
1029 end = alignmentAnnotation.annotations.length;
1031 if (hiddenColumns != null && hiddenColumns.size() > 0)
1033 // then mangle the alignmentAnnotation annotation array
1034 Vector<Annotation[]> annels = new Vector<>();
1035 Annotation[] els = null;
1036 List<int[]> regions = getHiddenRegions();
1037 int blockStart = start;
1044 for (int j = 0; j < regions.size(); j++)
1046 region = regions.get(j);
1047 hideStart = region[0];
1048 hideEnd = region[1];
1050 if (hideStart < start)
1055 blockStart = Math.min(blockStart, hideEnd + 1);
1056 blockEnd = Math.min(blockEnd, hideStart);
1058 if (blockStart > blockEnd)
1063 annels.addElement(els = new Annotation[blockEnd - blockStart]);
1064 System.arraycopy(alignmentAnnotation.annotations, blockStart, els,
1067 blockStart = hideEnd + 1;
1071 if (end > blockStart)
1073 annels.addElement(els = new Annotation[end - blockStart + 1]);
1075 + blockStart) <= alignmentAnnotation.annotations.length)
1077 // copy just the visible segment of the annotation row
1078 System.arraycopy(alignmentAnnotation.annotations, blockStart,
1079 els, 0, els.length);
1083 // copy to the end of the annotation row
1084 System.arraycopy(alignmentAnnotation.annotations, blockStart,
1086 (alignmentAnnotation.annotations.length - blockStart));
1095 alignmentAnnotation.annotations = new Annotation[w];
1098 for (Annotation[] chnk : annels)
1100 System.arraycopy(chnk, 0, alignmentAnnotation.annotations, w,
1107 alignmentAnnotation.restrict(start, end);
1111 LOCK.readLock().unlock();
1117 * @return true if there are columns hidden
1119 public boolean hasHiddenColumns()
1123 LOCK.readLock().lock();
1124 return hiddenColumns != null && hiddenColumns.size() > 0;
1127 LOCK.readLock().unlock();
1133 * @return true if there are more than one set of columns hidden
1135 public boolean hasManyHiddenColumns()
1139 LOCK.readLock().lock();
1140 return hiddenColumns != null && hiddenColumns.size() > 1;
1143 LOCK.readLock().unlock();
1148 * mark the columns corresponding to gap characters as hidden in the column
1153 public void hideInsertionsFor(SequenceI sr)
1157 LOCK.writeLock().lock();
1158 List<int[]> inserts = sr.getInsertions();
1159 for (int[] r : inserts)
1161 hideColumns(r[0], r[1]);
1165 LOCK.writeLock().unlock();
1170 * Unhides, and adds to the selection list, all hidden columns
1172 public void revealAllHiddenColumns(ColumnSelection sel)
1176 LOCK.writeLock().lock();
1177 if (hiddenColumns != null)
1179 for (int i = 0; i < hiddenColumns.size(); i++)
1181 int[] region = hiddenColumns.get(i);
1182 for (int j = region[0]; j < region[1] + 1; j++)
1189 hiddenColumns = null;
1192 LOCK.writeLock().unlock();
1197 * Reveals, and marks as selected, the hidden column range with the given
1202 public void revealHiddenColumns(int start, ColumnSelection sel)
1206 LOCK.writeLock().lock();
1207 for (int i = 0; i < hiddenColumns.size(); i++)
1209 int[] region = hiddenColumns.get(i);
1210 if (start == region[0])
1212 for (int j = region[0]; j < region[1] + 1; j++)
1217 hiddenColumns.remove(region);
1221 if (hiddenColumns.size() == 0)
1223 hiddenColumns = null;
1227 LOCK.writeLock().unlock();
1232 * Add gaps into the sequences aligned to profileseq under the given
1237 * - alignment to have gaps inserted into it
1239 * - alignment view where sequence corresponding to profileseq is
1241 * @return new HiddenColumns for new alignment view, with insertions into
1242 * profileseq marked as hidden.
1244 public static HiddenColumns propagateInsertions(SequenceI profileseq,
1245 AlignmentI al, AlignmentView input)
1249 char gc = al.getGapCharacter();
1250 Object[] alandhidden = input.getAlignmentAndHiddenColumns(gc);
1251 HiddenColumns nview = (HiddenColumns) alandhidden[1];
1252 SequenceI origseq = ((SequenceI[]) alandhidden[0])[profsqpos];
1253 nview.propagateInsertions(profileseq, al, origseq);
1260 * - sequence in al which corresponds to origseq
1262 * - alignment which is to have gaps inserted into it
1264 * - sequence corresponding to profileseq which defines gap map for
1267 private void propagateInsertions(SequenceI profileseq, AlignmentI al,
1270 char gc = al.getGapCharacter();
1272 // take the set of hidden columns, and the set of gaps in origseq,
1273 // and remove all the hidden gaps from hiddenColumns
1275 // first get the gaps as a Bitset
1276 BitSet gaps = origseq.gapBitset();
1278 // now calculate hidden ^ not(gap)
1279 BitSet hidden = new BitSet();
1280 markHiddenRegions(hidden);
1281 hidden.andNot(gaps);
1282 hiddenColumns = null;
1283 this.hideMarkedBits(hidden);
1285 // for each sequence in the alignment, except the profile sequence,
1286 // insert gaps corresponding to each hidden region
1287 // but where each hidden column region is shifted backwards by the number of
1288 // preceding visible gaps
1289 // update hidden columns at the same time
1290 ArrayList<int[]> regions = getHiddenColumnsCopy();
1291 ArrayList<int[]> newhidden = new ArrayList<>();
1293 int numGapsBefore = 0;
1294 int gapPosition = 0;
1295 for (int[] region : regions)
1297 // get region coordinates accounting for gaps
1298 // we can rely on gaps not being *in* hidden regions because we already
1300 while (gapPosition < region[0])
1303 if (gaps.get(gapPosition))
1309 int left = region[0] - numGapsBefore;
1310 int right = region[1] - numGapsBefore;
1311 newhidden.add(new int[] { left, right });
1313 // make a string with number of gaps = length of hidden region
1314 StringBuffer sb = new StringBuffer();
1315 for (int s = 0; s < right - left + 1; s++)
1319 padGaps(sb, left, profileseq, al);
1322 hiddenColumns = newhidden;
1326 * Pad gaps in all sequences in alignment except profileseq
1329 * gap string to insert
1331 * position to insert at
1333 * sequence not to pad
1335 * alignment to pad sequences in
1337 private void padGaps(StringBuffer sb, int pos, SequenceI profileseq,
1340 // loop over the sequences and pad with gaps where required
1341 for (int s = 0, ns = al.getHeight(); s < ns; s++)
1343 SequenceI sqobj = al.getSequenceAt(s);
1344 if (sqobj != profileseq)
1346 String sq = al.getSequenceAt(s).getSequenceAsString();
1347 if (sq.length() <= pos)
1350 int diff = pos - sq.length() - 1;
1355 while ((diff = pos - sq.length() - 1) > 0)
1357 if (diff >= sb.length())
1359 sq += sb.toString();
1363 char[] buf = new char[diff];
1364 sb.getChars(0, diff, buf, 0);
1365 sq += buf.toString();
1369 sq += sb.toString();
1373 al.getSequenceAt(s).setSequence(
1374 sq.substring(0, pos) + sb.toString() + sq.substring(pos));
1381 * Returns a hashCode built from hidden column ranges
1384 public int hashCode()
1388 LOCK.readLock().lock();
1390 if (hiddenColumns != null)
1392 for (int[] hidden : hiddenColumns)
1394 hashCode = 31 * hashCode + hidden[0];
1395 hashCode = 31 * hashCode + hidden[1];
1401 LOCK.readLock().unlock();
1406 * Hide columns corresponding to the marked bits
1409 * - columns map to bits starting from zero
1411 public void hideMarkedBits(BitSet inserts)
1415 LOCK.writeLock().lock();
1416 for (int firstSet = inserts
1417 .nextSetBit(0), lastSet = 0; firstSet >= 0; firstSet = inserts
1418 .nextSetBit(lastSet))
1420 lastSet = inserts.nextClearBit(firstSet);
1421 hideColumns(firstSet, lastSet - 1);
1425 LOCK.writeLock().unlock();
1432 * BitSet where hidden columns will be marked
1434 public void markHiddenRegions(BitSet inserts)
1438 LOCK.readLock().lock();
1439 if (hiddenColumns == null)
1443 for (int[] range : hiddenColumns)
1445 inserts.set(range[0], range[1] + 1);
1449 LOCK.readLock().unlock();
1454 * Calculate the visible start and end index of an alignment.
1457 * full alignment width
1458 * @return integer array where: int[0] = startIndex, and int[1] = endIndex
1460 public int[] getVisibleStartAndEndIndex(int width)
1464 LOCK.readLock().lock();
1465 int[] alignmentStartEnd = new int[] { 0, width - 1 };
1466 int startPos = alignmentStartEnd[0];
1467 int endPos = alignmentStartEnd[1];
1469 int[] lowestRange = new int[] { -1, -1 };
1470 int[] higestRange = new int[] { -1, -1 };
1472 if (hiddenColumns == null)
1474 return new int[] { startPos, endPos };
1477 for (int[] hiddenCol : hiddenColumns)
1479 lowestRange = (hiddenCol[0] <= startPos) ? hiddenCol : lowestRange;
1480 higestRange = (hiddenCol[1] >= endPos) ? hiddenCol : higestRange;
1483 if (lowestRange[0] == -1 && lowestRange[1] == -1)
1485 startPos = alignmentStartEnd[0];
1489 startPos = lowestRange[1] + 1;
1492 if (higestRange[0] == -1 && higestRange[1] == -1)
1494 endPos = alignmentStartEnd[1];
1498 endPos = higestRange[0] - 1;
1500 return new int[] { startPos, endPos };
1503 LOCK.readLock().unlock();
1509 * Finds the hidden region (if any) which starts or ends at res
1512 * visible residue position, unadjusted for hidden columns
1513 * @return region as [start,end] or null if no matching region is found
1515 public int[] getRegionWithEdgeAtRes(int res)
1519 LOCK.readLock().lock();
1520 int adjres = adjustForHiddenColumns(res);
1522 int[] reveal = null;
1523 if (hiddenColumns != null)
1525 for (int[] region : hiddenColumns)
1527 if (adjres + 1 == region[0] || adjres - 1 == region[1])
1537 LOCK.readLock().unlock();