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;
24 import jalview.util.ShiftList;
26 import java.util.ArrayList;
27 import java.util.BitSet;
28 import java.util.Collections;
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 ReentrantReadWriteLock LOCK = new ReentrantReadWriteLock();
38 * list of hidden column [start, end] ranges; the list is maintained in
39 * ascending start column order
41 private ArrayList<int[]> hiddenColumns;
46 public HiddenColumns()
55 public HiddenColumns(HiddenColumns copy)
60 LOCK.readLock().lock();
63 if (copy.hiddenColumns != null)
65 hiddenColumns = copy.copyHiddenRegionsToArrayList();
70 LOCK.readLock().unlock();
75 * This method is used to return all the HiddenColumn regions and is intended
76 * to remain private. External callers which need a copy of the regions can
77 * call getHiddenColumnsCopyAsList.
79 * @return empty list or List of hidden column intervals
81 private List<int[]> getHiddenRegions()
83 return hiddenColumns == null ? Collections.<int[]> emptyList()
88 * Output regions data as a string. String is in the format:
89 * reg0[0]<between>reg0[1]<delimiter>reg1[0]<between>reg1[1] ... regn[1]
92 * string to delimit regions
93 * @param betweenstring
94 * to put between start and end region values
95 * @return regions formatted according to delimiter and between strings
97 public String regionsToString(String delimiter, String between)
101 LOCK.readLock().lock();
102 StringBuilder regionBuilder = new StringBuilder();
103 if (hiddenColumns != null)
105 for (int[] range : hiddenColumns)
107 regionBuilder.append(delimiter).append(range[0]).append(between)
111 regionBuilder.deleteCharAt(0);
113 return regionBuilder.toString();
116 LOCK.readLock().unlock();
121 * Find the number of hidden columns
123 * @return number of hidden columns
129 LOCK.readLock().lock();
133 for (int[] range : hiddenColumns)
135 size += range[1] - range[0] + 1;
142 LOCK.readLock().unlock();
147 * Answers if there are any hidden columns
149 * @return true if there are hidden columns
151 public boolean hasHidden()
155 LOCK.readLock().lock();
156 return (hiddenColumns != null) && (!hiddenColumns.isEmpty());
159 LOCK.readLock().unlock();
165 public boolean equals(Object obj)
169 LOCK.readLock().lock();
171 if (!(obj instanceof HiddenColumns))
175 HiddenColumns that = (HiddenColumns) obj;
178 * check hidden columns are either both null, or match
180 if (this.hiddenColumns == null)
182 return (that.hiddenColumns == null);
184 if (that.hiddenColumns == null
185 || that.hiddenColumns.size() != this.hiddenColumns.size())
190 for (int[] thisRange : hiddenColumns)
192 int[] thatRange = that.hiddenColumns.get(i++);
193 if (thisRange[0] != thatRange[0] || thisRange[1] != thatRange[1])
201 LOCK.readLock().unlock();
206 * Return absolute column index for a visible column index
209 * int column index in alignment view (count from zero)
210 * @return alignment column index for column
212 public int adjustForHiddenColumns(int column)
216 LOCK.readLock().lock();
218 if (hiddenColumns != null)
220 for (int i = 0; i < hiddenColumns.size(); i++)
222 int[] region = hiddenColumns.get(i);
223 if (result >= region[0])
225 result += region[1] - region[0] + 1;
232 LOCK.readLock().unlock();
237 * Use this method to find out where a column will appear in the visible
238 * alignment when hidden columns exist. If the column is not visible, then the
239 * left-most visible column will always be returned.
241 * @param hiddenColumn
242 * the column index in the full alignment including hidden columns
243 * @return the position of the column in the visible alignment
245 public int findColumnPosition(int hiddenColumn)
249 LOCK.readLock().lock();
250 int result = hiddenColumn;
251 if (hiddenColumns != null)
257 region = hiddenColumns.get(index++);
258 if (hiddenColumn > region[1])
260 result -= region[1] + 1 - region[0];
262 } while ((hiddenColumn > region[1])
263 && (index < hiddenColumns.size()));
265 if (hiddenColumn >= region[0] && hiddenColumn <= region[1])
267 // Here the hidden column is within a region, so
268 // we want to return the position of region[0]-1, adjusted for any
269 // earlier hidden columns.
270 // Calculate the difference between the actual hidden col position
271 // and region[0]-1, and then subtract from result to convert result
273 // the adjusted hiddenColumn value to the adjusted region[0]-1 value
275 // However, if the region begins at 0 we cannot return region[0]-1
283 return result - (hiddenColumn - region[0] + 1);
287 return result; // return the shifted position after removing hidden
291 LOCK.readLock().unlock();
296 * Find the visible column which is a given visible number of columns to the
297 * left of another visible column. i.e. for a startColumn x, the column which
298 * is distance 1 away will be column x-1.
300 * @param visibleDistance
301 * the number of visible columns to offset by
303 * the column to start from
304 * @return the position of the column in the visible alignment
306 public int subtractVisibleColumns(int visibleDistance, int startColumn)
311 LOCK.readLock().lock();
312 int distance = visibleDistance;
314 // in case startColumn is in a hidden region, move it to the left
315 int start = adjustForHiddenColumns(findColumnPosition(startColumn));
317 // get index of hidden region to left of start
318 int index = getHiddenIndexLeft(start);
321 // no hidden regions to left of startColumn
322 return start - distance;
325 // walk backwards through the alignment subtracting the counts of visible
326 // columns from distance
329 int nextstart = start;
331 while ((index > -1) && (distance - gap > 0))
333 // subtract the gap to right of region from distance
337 // calculate the next gap
338 region = hiddenColumns.get(index);
339 gap = start - region[1];
341 // set start to just to left of current region
342 nextstart = region[0] - 1;
346 if (distance - gap > 0)
348 // fell out of loop because there are no more hidden regions
350 return nextstart - distance;
352 return start - distance;
355 LOCK.readLock().unlock();
361 * Use this method to determine the set of hiddenRegion start positions
363 * @return list of column number in visible view where hidden regions start
365 public List<Integer> findHiddenRegionPositions()
369 LOCK.readLock().lock();
370 List<Integer> positions = null;
372 if (hiddenColumns != null)
374 positions = new ArrayList<>(hiddenColumns.size());
376 positions.add(hiddenColumns.get(0)[0]);
377 for (int i = 1; i < hiddenColumns.size(); ++i)
381 if (hiddenColumns != null)
387 int[] region = hiddenColumns.get(index);
388 gaps += region[1] + 1 - region[0];
389 result = region[1] + 1;
391 } while (index <= i);
395 positions.add(result);
400 positions = new ArrayList<>();
407 LOCK.readLock().unlock();
412 * This method returns the rightmost limit of a region of an alignment with
413 * hidden columns. In otherwords, the next hidden column.
418 public int getHiddenBoundaryRight(int alPos)
422 LOCK.readLock().lock();
423 if (hiddenColumns != null)
428 int[] region = hiddenColumns.get(index);
429 if (alPos < region[0])
435 } while (index < hiddenColumns.size());
441 LOCK.readLock().unlock();
447 * This method returns the leftmost limit of a region of an alignment with
448 * hidden columns. In otherwords, the previous hidden column.
453 public int getHiddenBoundaryLeft(int alPos)
457 LOCK.readLock().lock();
459 if (hiddenColumns != null)
461 int index = hiddenColumns.size() - 1;
464 int[] region = hiddenColumns.get(index);
465 if (alPos > region[1])
471 } while (index > -1);
477 LOCK.readLock().unlock();
482 * This method returns the index of the hidden region to the left of a column
483 * position. If the column is in a hidden region it returns the index of the
484 * region to the left. If there is no hidden region to the left it returns -1.
489 private int getHiddenIndexLeft(int pos)
494 LOCK.readLock().lock();
495 if (hiddenColumns != null)
497 int index = hiddenColumns.size() - 1;
500 int[] region = hiddenColumns.get(index);
507 } while (index > -1);
513 LOCK.readLock().unlock();
519 * Adds the specified column range to the hidden columns
524 public void hideColumns(int start, int end)
526 hideColumns(start, end, false);
530 * Adds the specified column range to the hidden columns
535 private void hideColumns(int start, int end, boolean alreadyLocked)
542 LOCK.writeLock().lock();
545 if (hiddenColumns == null)
547 hiddenColumns = new ArrayList<>();
551 * traverse existing hidden ranges and insert / amend / append as
554 for (int i = 0; i < hiddenColumns.size(); i++)
556 int[] region = hiddenColumns.get(i);
558 if (end < region[0] - 1)
561 * insert discontiguous preceding range
563 hiddenColumns.add(i, new int[] { start, end });
567 if (end <= region[1])
570 * new range overlaps existing, or is contiguous preceding it - adjust
573 region[0] = Math.min(region[0], start);
577 if (start <= region[1] + 1)
580 * new range overlaps existing, or is contiguous following it - adjust
581 * start and end columns
583 region[0] = Math.min(region[0], start);
584 region[1] = Math.max(region[1], end);
587 * also update or remove any subsequent ranges
588 * that are overlapped
590 while (i < hiddenColumns.size() - 1)
592 int[] nextRegion = hiddenColumns.get(i + 1);
593 if (nextRegion[0] > end + 1)
596 * gap to next hidden range - no more to update
600 region[1] = Math.max(nextRegion[1], end);
601 hiddenColumns.remove(i + 1);
608 * remaining case is that the new range follows everything else
610 hiddenColumns.add(new int[] { start, end });
615 LOCK.writeLock().unlock();
620 public boolean isVisible(int column)
624 LOCK.readLock().lock();
626 if (hiddenColumns != null)
628 for (int[] region : hiddenColumns)
630 if (column >= region[0] && column <= region[1])
640 LOCK.readLock().unlock();
644 private ArrayList<int[]> copyHiddenRegionsToArrayList()
647 if (hiddenColumns != null)
649 size = hiddenColumns.size();
651 ArrayList<int[]> copy = new ArrayList<>(size);
653 for (int i = 0, j = size; i < j; i++)
657 rh = hiddenColumns.get(i);
660 cp = new int[rh.length];
661 System.arraycopy(rh, 0, cp, 0, rh.length);
670 * Returns a copy of the vector of hidden regions, as an ArrayList. Before
671 * using this method please consider if you really need access to the hidden
672 * regions - a new (or existing!) method on HiddenColumns might be more
675 * @return hidden regions as an ArrayList of [start,end] pairs
677 public ArrayList<int[]> getHiddenColumnsCopy()
681 LOCK.readLock().lock();
682 return copyHiddenRegionsToArrayList();
685 LOCK.readLock().unlock();
690 * propagate shift in alignment columns to column selection
695 * shift in edit (+ve for removal, or -ve for inserts)
697 public List<int[]> compensateForEdit(int start, int change,
702 LOCK.writeLock().lock();
703 List<int[]> deletedHiddenColumns = null;
705 if (hiddenColumns != null)
707 deletedHiddenColumns = new ArrayList<>();
708 int hSize = hiddenColumns.size();
709 for (int i = 0; i < hSize; i++)
711 int[] region = hiddenColumns.get(i);
712 if (region[0] > start && start + change > region[1])
714 deletedHiddenColumns.add(region);
716 hiddenColumns.remove(i);
722 if (region[0] > start)
735 this.revealHiddenColumns(0, sel);
738 return deletedHiddenColumns;
741 LOCK.writeLock().unlock();
746 * propagate shift in alignment columns to column selection special version of
747 * compensateForEdit - allowing for edits within hidden regions
752 * shift in edit (+ve for removal, or -ve for inserts)
754 public void compensateForDelEdits(int start, int change)
758 LOCK.writeLock().lock();
759 if (hiddenColumns != null)
761 for (int i = 0; i < hiddenColumns.size(); i++)
763 int[] region = hiddenColumns.get(i);
764 if (region[0] >= start)
768 if (region[1] >= start)
772 if (region[1] < region[0])
774 hiddenColumns.remove(i--);
790 LOCK.writeLock().unlock();
795 * return all visible segments between the given start and end boundaries
798 * (first column inclusive from 0)
800 * (last column - not inclusive)
801 * @return int[] {i_start, i_end, ..} where intervals lie in
802 * start<=i_start<=i_end<end
804 public int[] getVisibleContigs(int start, int end)
808 LOCK.readLock().lock();
809 if (hiddenColumns != null && hiddenColumns.size() > 0)
811 List<int[]> visiblecontigs = new ArrayList<>();
812 List<int[]> regions = getHiddenRegions();
819 for (int j = 0; vstart < end && j < regions.size(); j++)
821 region = regions.get(j);
822 hideStart = region[0];
825 if (hideEnd < vstart)
829 if (hideStart > vstart)
831 visiblecontigs.add(new int[] { vstart, hideStart - 1 });
833 vstart = hideEnd + 1;
838 visiblecontigs.add(new int[] { vstart, end - 1 });
840 int[] vcontigs = new int[visiblecontigs.size() * 2];
841 for (int i = 0, j = visiblecontigs.size(); i < j; i++)
843 int[] vc = visiblecontigs.get(i);
844 visiblecontigs.set(i, null);
845 vcontigs[i * 2] = vc[0];
846 vcontigs[i * 2 + 1] = vc[1];
848 visiblecontigs.clear();
853 return new int[] { start, end - 1 };
858 LOCK.readLock().unlock();
862 public String[] getVisibleSequenceStrings(int start, int end,
867 LOCK.readLock().lock();
868 int iSize = seqs.length;
869 String[] selections = new String[iSize];
870 if (hiddenColumns != null && hiddenColumns.size() > 0)
872 for (int i = 0; i < iSize; i++)
874 StringBuffer visibleSeq = new StringBuffer();
875 List<int[]> regions = getHiddenRegions();
877 int blockStart = start;
883 for (int j = 0; j < regions.size(); j++)
885 region = regions.get(j);
886 hideStart = region[0];
889 if (hideStart < start)
894 blockStart = Math.min(blockStart, hideEnd + 1);
895 blockEnd = Math.min(blockEnd, hideStart);
897 if (blockStart > blockEnd)
902 visibleSeq.append(seqs[i].getSequence(blockStart, blockEnd));
904 blockStart = hideEnd + 1;
908 if (end > blockStart)
910 visibleSeq.append(seqs[i].getSequence(blockStart, end));
913 selections[i] = visibleSeq.toString();
918 for (int i = 0; i < iSize; i++)
920 selections[i] = seqs[i].getSequenceAsString(start, end);
928 LOCK.readLock().unlock();
933 * Locate the first and last position visible for this sequence. if seq isn't
934 * visible then return the position of the left and right of the hidden
935 * boundary region, and the corresponding alignment column indices for the
936 * extent of the sequence
939 * @return int[] { visible start, visible end, first seqpos, last seqpos,
940 * alignment index for seq start, alignment index for seq end }
942 public int[] locateVisibleBoundsOfSequence(SequenceI seq)
946 LOCK.readLock().lock();
947 int fpos = seq.getStart();
948 int lpos = seq.getEnd();
951 if (hiddenColumns == null || hiddenColumns.size() == 0)
953 int ifpos = seq.findIndex(fpos) - 1;
954 int ilpos = seq.findIndex(lpos) - 1;
955 return new int[] { ifpos, ilpos, fpos, lpos, ifpos, ilpos };
958 // Simply walk along the sequence whilst watching for hidden column
960 List<int[]> regions = getHiddenRegions();
964 int hideStart = seq.getLength();
970 boolean foundStart = false;
971 for (int p = 0, pLen = seq.getLength(); spos <= seq.getEnd()
974 if (!Comparison.isGap(seq.getCharAt(p)))
976 // keep track of first/last column
977 // containing sequence data regardless of visibility
983 // update hidden region start/end
984 while (hideEnd < p && rcount < regions.size())
986 int[] region = regions.get(rcount++);
988 visNext += region[0] - visPrev;
989 hideStart = region[0];
994 hideStart = seq.getLength();
996 // update visible boundary for sequence
1008 // look for next sequence position
1014 return new int[] { findColumnPosition(start),
1015 findColumnPosition(lastvispos), fpos, lpos, firstP, lastP };
1017 // otherwise, sequence was completely hidden
1018 return new int[] { visPrev, visNext, 0, 0, firstP, lastP };
1022 LOCK.readLock().unlock();
1027 * delete any columns in alignmentAnnotation that are hidden (including
1028 * sequence associated annotation).
1030 * @param alignmentAnnotation
1032 public void makeVisibleAnnotation(AlignmentAnnotation alignmentAnnotation)
1034 makeVisibleAnnotation(-1, -1, alignmentAnnotation);
1038 * delete any columns in alignmentAnnotation that are hidden (including
1039 * sequence associated annotation).
1042 * remove any annotation to the right of this column
1044 * remove any annotation to the left of this column
1045 * @param alignmentAnnotation
1046 * the annotation to operate on
1048 public void makeVisibleAnnotation(int start, int end,
1049 AlignmentAnnotation alignmentAnnotation)
1053 LOCK.readLock().lock();
1054 if (alignmentAnnotation.annotations == null)
1058 if (start == end && end == -1)
1061 end = alignmentAnnotation.annotations.length;
1063 if (hiddenColumns != null && hiddenColumns.size() > 0)
1065 // then mangle the alignmentAnnotation annotation array
1066 Vector<Annotation[]> annels = new Vector<>();
1067 Annotation[] els = null;
1068 List<int[]> regions = getHiddenRegions();
1069 int blockStart = start;
1076 for (int j = 0; j < regions.size(); j++)
1078 region = regions.get(j);
1079 hideStart = region[0];
1080 hideEnd = region[1];
1082 if (hideStart < start)
1087 blockStart = Math.min(blockStart, hideEnd + 1);
1088 blockEnd = Math.min(blockEnd, hideStart);
1090 if (blockStart > blockEnd)
1095 annels.addElement(els = new Annotation[blockEnd - blockStart]);
1096 System.arraycopy(alignmentAnnotation.annotations, blockStart, els,
1099 blockStart = hideEnd + 1;
1103 if (end > blockStart)
1105 annels.addElement(els = new Annotation[end - blockStart + 1]);
1107 + blockStart) <= alignmentAnnotation.annotations.length)
1109 // copy just the visible segment of the annotation row
1110 System.arraycopy(alignmentAnnotation.annotations, blockStart,
1111 els, 0, els.length);
1115 // copy to the end of the annotation row
1116 System.arraycopy(alignmentAnnotation.annotations, blockStart,
1118 (alignmentAnnotation.annotations.length - blockStart));
1127 alignmentAnnotation.annotations = new Annotation[w];
1130 for (Annotation[] chnk : annels)
1132 System.arraycopy(chnk, 0, alignmentAnnotation.annotations, w,
1139 alignmentAnnotation.restrict(start, end);
1144 LOCK.readLock().unlock();
1150 * @return true if there are columns hidden
1152 public boolean hasHiddenColumns()
1156 LOCK.readLock().lock();
1157 return hiddenColumns != null && hiddenColumns.size() > 0;
1160 LOCK.readLock().unlock();
1166 * @return true if there are more than one set of columns hidden
1168 public boolean hasManyHiddenColumns()
1172 LOCK.readLock().lock();
1173 return hiddenColumns != null && hiddenColumns.size() > 1;
1176 LOCK.readLock().unlock();
1181 * mark the columns corresponding to gap characters as hidden in the column
1186 public void hideInsertionsFor(SequenceI sr)
1190 LOCK.writeLock().lock();
1191 List<int[]> inserts = sr.getInsertions();
1192 for (int[] r : inserts)
1194 hideColumns(r[0], r[1], true);
1198 LOCK.writeLock().unlock();
1203 * Unhides, and adds to the selection list, all hidden columns
1205 public void revealAllHiddenColumns(ColumnSelection sel)
1209 LOCK.writeLock().lock();
1210 if (hiddenColumns != null)
1212 for (int i = 0; i < hiddenColumns.size(); i++)
1214 int[] region = hiddenColumns.get(i);
1215 for (int j = region[0]; j < region[1] + 1; j++)
1222 hiddenColumns = null;
1226 LOCK.writeLock().unlock();
1231 * Reveals, and marks as selected, the hidden column range with the given
1236 public void revealHiddenColumns(int start, ColumnSelection sel)
1240 LOCK.writeLock().lock();
1241 for (int i = 0; i < hiddenColumns.size(); i++)
1243 int[] region = hiddenColumns.get(i);
1244 if (start == region[0])
1246 for (int j = region[0]; j < region[1] + 1; j++)
1251 hiddenColumns.remove(region);
1255 if (hiddenColumns.size() == 0)
1257 hiddenColumns = null;
1262 LOCK.writeLock().unlock();
1267 * removes intersection of position,length ranges in deletions from the
1268 * start,end regions marked in intervals.
1274 private boolean pruneIntervalList(final List<int[]> shifts,
1275 ArrayList<int[]> intervals)
1277 boolean pruned = false;
1279 int j = intervals.size() - 1;
1281 int t = shifts.size() - 1;
1282 int[] hr = intervals.get(i);
1283 int[] sr = shifts.get(s);
1284 while (i <= j && s <= t)
1286 boolean trailinghn = hr[1] >= sr[0];
1291 hr = intervals.get(++i);
1299 int endshift = sr[0] + sr[1]; // deletion ranges - -ve means an insert
1300 if (endshift < hr[0] || endshift < sr[0])
1301 { // leadinghc disjoint or not a deletion
1304 sr = shifts.get(++s);
1312 boolean leadinghn = hr[0] >= sr[0];
1313 boolean leadinghc = hr[0] < endshift;
1314 boolean trailinghc = hr[1] < endshift;
1318 { // deleted hidden region.
1319 intervals.remove(i);
1324 hr = intervals.get(i);
1330 hr[0] = endshift; // clip c terminal region
1331 leadinghn = !leadinghn;
1347 // sr contained in hr
1350 sr = shifts.get(++s);
1360 return pruned; // true if any interval was removed or modified by
1365 * remove any hiddenColumns or selected columns and shift remaining based on a
1366 * series of position, range deletions.
1370 public void pruneDeletions(List<int[]> shifts)
1374 LOCK.writeLock().lock();
1375 // delete any intervals intersecting.
1376 if (hiddenColumns != null)
1378 pruneIntervalList(shifts, hiddenColumns);
1379 if (hiddenColumns != null && hiddenColumns.size() == 0)
1381 hiddenColumns = null;
1387 LOCK.writeLock().unlock();
1392 * Add gaps into the sequences aligned to profileseq under the given
1397 * - alignment to have gaps inserted into it
1399 * - alignment view where sequence corresponding to profileseq is
1401 * @return new HiddenColumns for new alignment view, with insertions into
1402 * profileseq marked as hidden.
1404 public static HiddenColumns propagateInsertions(SequenceI profileseq,
1405 AlignmentI al, AlignmentView input)
1409 char gc = al.getGapCharacter();
1410 Object[] alandhidden = input.getAlignmentAndHiddenColumns(gc);
1411 HiddenColumns nview = (HiddenColumns) alandhidden[1];
1412 SequenceI origseq = ((SequenceI[]) alandhidden[0])[profsqpos];
1413 nview.propagateInsertions(profileseq, al, origseq);
1420 * - sequence in al which corresponds to origseq
1422 * - alignment which is to have gaps inserted into it
1424 * - sequence corresponding to profileseq which defines gap map for
1427 private void propagateInsertions(SequenceI profileseq, AlignmentI al,
1430 char gc = al.getGapCharacter();
1431 // recover mapping between sequence's non-gap positions and positions
1433 pruneDeletions(ShiftList.parseMap(origseq.gapMap()));
1434 int[] viscontigs = al.getHiddenColumns().getVisibleContigs(0,
1435 profileseq.getLength());
1439 // add profile to visible contigs
1440 for (int v = 0; v < viscontigs.length; v += 2)
1442 if (viscontigs[v] > spos)
1444 StringBuffer sb = new StringBuffer();
1445 for (int s = 0, ns = viscontigs[v] - spos; s < ns; s++)
1449 for (int s = 0, ns = al.getHeight(); s < ns; s++)
1451 SequenceI sqobj = al.getSequenceAt(s);
1452 if (sqobj != profileseq)
1454 String sq = al.getSequenceAt(s).getSequenceAsString();
1455 if (sq.length() <= spos + offset)
1458 int diff = spos + offset - sq.length() - 1;
1463 while ((diff = spos + offset - sq.length() - 1) > 0)
1466 // + ((diff >= sb.length()) ? sb.toString() : sb
1467 // .substring(0, diff));
1468 if (diff >= sb.length())
1470 sq += sb.toString();
1474 char[] buf = new char[diff];
1475 sb.getChars(0, diff, buf, 0);
1476 sq += buf.toString();
1480 sq += sb.toString();
1484 al.getSequenceAt(s).setSequence(
1485 sq.substring(0, spos + offset) + sb.toString()
1486 + sq.substring(spos + offset));
1490 // offset+=sb.length();
1492 spos = viscontigs[v + 1] + 1;
1494 if ((offset + spos) < profileseq.getLength())
1496 // pad the final region with gaps.
1497 StringBuffer sb = new StringBuffer();
1498 for (int s = 0, ns = profileseq.getLength() - spos - offset; s < ns; s++)
1502 for (int s = 0, ns = al.getHeight(); s < ns; s++)
1504 SequenceI sqobj = al.getSequenceAt(s);
1505 if (sqobj == profileseq)
1509 String sq = sqobj.getSequenceAsString();
1511 int diff = origseq.getLength() - sq.length();
1515 // + ((diff >= sb.length()) ? sb.toString() : sb
1516 // .substring(0, diff));
1517 if (diff >= sb.length())
1519 sq += sb.toString();
1523 char[] buf = new char[diff];
1524 sb.getChars(0, diff, buf, 0);
1525 sq += buf.toString();
1527 diff = origseq.getLength() - sq.length();
1534 * remove any hiddenColumns or selected columns and shift remaining based on a
1535 * series of position, range deletions.
1539 private void pruneDeletions(ShiftList deletions)
1541 if (deletions != null)
1543 final List<int[]> shifts = deletions.getShifts();
1544 if (shifts != null && shifts.size() > 0)
1546 pruneDeletions(shifts);
1548 // and shift the rest.
1549 this.compensateForEdits(deletions);
1555 * Adjust hidden column boundaries based on a series of column additions or
1556 * deletions in visible regions.
1558 * @param shiftrecord
1561 private ShiftList compensateForEdits(ShiftList shiftrecord)
1563 if (shiftrecord != null)
1565 final List<int[]> shifts = shiftrecord.getShifts();
1566 if (shifts != null && shifts.size() > 0)
1569 for (int i = 0, j = shifts.size(); i < j; i++)
1571 int[] sh = shifts.get(i);
1572 compensateForDelEdits(shifted + sh[0], sh[1]);
1576 return shiftrecord.getInverse();
1582 * Returns a hashCode built from hidden column ranges
1585 public int hashCode()
1589 LOCK.readLock().lock();
1591 if (hiddenColumns != null)
1593 for (int[] hidden : hiddenColumns)
1595 hashCode = 31 * hashCode + hidden[0];
1596 hashCode = 31 * hashCode + hidden[1];
1603 LOCK.readLock().unlock();
1608 * Hide columns corresponding to the marked bits
1611 * - columns map to bits starting from zero
1613 public void hideMarkedBits(BitSet inserts)
1617 LOCK.writeLock().lock();
1618 for (int firstSet = inserts
1619 .nextSetBit(0), lastSet = 0; firstSet >= 0; firstSet = inserts
1620 .nextSetBit(lastSet))
1622 lastSet = inserts.nextClearBit(firstSet);
1623 hideColumns(firstSet, lastSet - 1, true);
1627 LOCK.writeLock().unlock();
1634 * BitSet where hidden columns will be marked
1636 public void markHiddenRegions(BitSet inserts)
1640 LOCK.readLock().lock();
1641 if (hiddenColumns == null)
1645 for (int[] range : hiddenColumns)
1647 inserts.set(range[0], range[1] + 1);
1652 LOCK.readLock().unlock();
1657 * Calculate the visible start and end index of an alignment.
1660 * full alignment width
1661 * @return integer array where: int[0] = startIndex, and int[1] = endIndex
1663 public int[] getVisibleStartAndEndIndex(int width)
1667 LOCK.readLock().lock();
1668 int[] alignmentStartEnd = new int[] { 0, width - 1 };
1669 int startPos = alignmentStartEnd[0];
1670 int endPos = alignmentStartEnd[1];
1672 int[] lowestRange = new int[] { -1, -1 };
1673 int[] higestRange = new int[] { -1, -1 };
1675 if (hiddenColumns == null)
1677 return new int[] { startPos, endPos };
1680 for (int[] hiddenCol : hiddenColumns)
1682 lowestRange = (hiddenCol[0] <= startPos) ? hiddenCol : lowestRange;
1683 higestRange = (hiddenCol[1] >= endPos) ? hiddenCol : higestRange;
1686 if (lowestRange[0] == -1 && lowestRange[1] == -1)
1688 startPos = alignmentStartEnd[0];
1692 startPos = lowestRange[1] + 1;
1695 if (higestRange[0] == -1 && higestRange[1] == -1)
1697 endPos = alignmentStartEnd[1];
1701 endPos = higestRange[0] - 1;
1703 return new int[] { startPos, endPos };
1706 LOCK.readLock().unlock();
1712 * Finds the hidden region (if any) which starts or ends at res
1715 * visible residue position, unadjusted for hidden columns
1716 * @return region as [start,end] or null if no matching region is found
1718 public int[] getRegionWithEdgeAtRes(int res)
1722 LOCK.readLock().lock();
1723 int adjres = adjustForHiddenColumns(res);
1725 int[] reveal = null;
1726 if (hiddenColumns != null)
1728 for (int[] region : hiddenColumns)
1730 if (adjres + 1 == region[0] || adjres - 1 == region[1])
1740 LOCK.readLock().unlock();