2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
3 * Copyright (C) 2012 J Procter, AM Waterhouse, LM Lui, J Engelhardt, G Barton, M Clamp, S Searle
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 of the License, or (at your option) any later version.
11 * Jalview is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty
13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along with Jalview. If not, see <http://www.gnu.org/licenses/>.
18 package jalview.datamodel;
22 import jalview.util.*;
25 * NOTE: Columns are zero based.
27 public class ColumnSelection
29 Vector selected = new Vector();
31 // Vector of int [] {startCol, endCol}
35 * Add a column to the selection
40 public void addElement(int col)
42 Integer column = new Integer(col);
43 if (!selected.contains(column))
45 selected.addElement(column);
50 * clears column selection
54 selected.removeAllElements();
58 * removes col from selection
61 * index of column to be removed
63 public void removeElement(int col)
65 Integer colInt = new Integer(col);
67 if (selected.contains(colInt))
69 selected.removeElement(colInt);
74 * removes a range of columns from the selection
77 * int - first column in range to be removed
81 public void removeElements(int start, int end)
84 for (int i = start; i < end; i++)
86 colInt = new Integer(i);
87 if (selected.contains(colInt))
89 selected.removeElement(colInt);
96 * @return Vector containing selected columns as Integers
98 public Vector getSelected()
106 * index to search for in column selection
108 * @return true if Integer(col) is in selection.
110 public boolean contains(int col)
112 return selected.contains(new Integer(col));
116 * Column number at position i in selection
119 * index into selected columns
121 * @return column number in alignment
123 public int columnAt(int i)
125 return ((Integer) selected.elementAt(i)).intValue();
131 * @return DOCUMENT ME!
135 return selected.size();
139 * rightmost selected column
141 * @return rightmost column in alignment that is selected
147 for (int i = 0; i < selected.size(); i++)
149 if (columnAt(i) > max)
159 * Leftmost column in selection
161 * @return column index of leftmost column in selection
165 int min = 1000000000;
167 for (int i = 0; i < selected.size(); i++)
169 if (columnAt(i) < min)
179 * propagate shift in alignment columns to column selection
184 * shift in edit (+ve for removal, or -ve for inserts)
186 public Vector compensateForEdit(int start, int change)
188 Vector deletedHiddenColumns = null;
189 for (int i = 0; i < size(); i++)
191 int temp = columnAt(i);
195 selected.setElementAt(new Integer(temp - change), i);
199 if (hiddenColumns != null)
201 deletedHiddenColumns = new Vector();
202 int hSize = hiddenColumns.size();
203 for (int i = 0; i < hSize; i++)
205 int[] region = (int[]) hiddenColumns.elementAt(i);
206 if (region[0] > start && start + change > region[1])
208 deletedHiddenColumns.addElement(hiddenColumns.elementAt(i));
210 hiddenColumns.removeElementAt(i);
216 if (region[0] > start)
229 this.revealHiddenColumns(0);
232 return deletedHiddenColumns;
236 * propagate shift in alignment columns to column selection special version of
237 * compensateForEdit - allowing for edits within hidden regions
242 * shift in edit (+ve for removal, or -ve for inserts)
244 private void compensateForDelEdits(int start, int change)
246 for (int i = 0; i < size(); i++)
248 int temp = columnAt(i);
252 selected.setElementAt(new Integer(temp - change), i);
256 if (hiddenColumns != null)
258 for (int i = 0; i < hiddenColumns.size(); i++)
260 int[] region = (int[]) hiddenColumns.elementAt(i);
261 if (region[0] >= start)
265 if (region[1] >= start)
269 if (region[1] < region[0])
271 hiddenColumns.removeElementAt(i--);
287 * Adjust hidden column boundaries based on a series of column additions or
288 * deletions in visible regions.
293 public ShiftList compensateForEdits(ShiftList shiftrecord)
295 if (shiftrecord != null)
297 Vector shifts = shiftrecord.shifts;
298 if (shifts != null && shifts.size() > 0)
301 for (int i = 0, j = shifts.size(); i < j; i++)
303 int[] sh = (int[]) shifts.elementAt(i);
304 // compensateForEdit(shifted+sh[0], sh[1]);
305 compensateForDelEdits(shifted + sh[0], sh[1]);
309 return shiftrecord.getInverse();
315 * removes intersection of position,length ranges in deletions from the
316 * start,end regions marked in intervals.
322 private boolean pruneIntervalVector(Vector deletions, Vector intervals)
324 boolean pruned = false;
325 int i = 0, j = intervals.size() - 1, s = 0, t = deletions.size() - 1;
326 int hr[] = (int[]) intervals.elementAt(i);
327 int sr[] = (int[]) deletions.elementAt(s);
328 while (i <= j && s <= t)
330 boolean trailinghn = hr[1] >= sr[0];
335 hr = (int[]) intervals.elementAt(++i);
343 int endshift = sr[0] + sr[1]; // deletion ranges - -ve means an insert
344 if (endshift < hr[0] || endshift < sr[0])
345 { // leadinghc disjoint or not a deletion
348 sr = (int[]) deletions.elementAt(++s);
356 boolean leadinghn = hr[0] >= sr[0];
357 boolean leadinghc = hr[0] < endshift;
358 boolean trailinghc = hr[1] < endshift;
362 { // deleted hidden region.
363 intervals.removeElementAt(i);
368 hr = (int[]) intervals.elementAt(i);
374 hr[0] = endshift; // clip c terminal region
375 leadinghn = !leadinghn;
391 // sr contained in hr
394 sr = (int[]) deletions.elementAt(++s);
404 return pruned; // true if any interval was removed or modified by
408 private boolean pruneColumnList(Vector deletion, Vector list)
410 int s = 0, t = deletion.size();
411 int[] sr = (int[]) list.elementAt(s++);
412 boolean pruned = false;
413 int i = 0, j = list.size();
414 while (i < j && s <= t)
416 int c = ((Integer) list.elementAt(i++)).intValue();
419 if (sr[1] + sr[0] >= c)
420 { // sr[1] -ve means inseriton.
421 list.removeElementAt(--i);
428 sr = (int[]) deletion.elementAt(s);
438 * remove any hiddenColumns or selected columns and shift remaining based on a
439 * series of position, range deletions.
443 public void pruneDeletions(ShiftList deletions)
445 if (deletions != null)
447 Vector shifts = deletions.shifts;
448 if (shifts != null && shifts.size() > 0)
450 // delete any intervals intersecting.
451 if (hiddenColumns != null)
453 pruneIntervalVector(shifts, hiddenColumns);
454 if (hiddenColumns != null && hiddenColumns.size() == 0)
456 hiddenColumns = null;
459 if (selected != null && selected.size() > 0)
461 pruneColumnList(shifts, selected);
462 if (selected != null && selected.size() == 0)
467 // and shift the rest.
468 this.compensateForEdits(deletions);
474 * This Method is used to return all the HiddenColumn regions less than the
481 public Vector getHiddenColumns()
483 return hiddenColumns;
487 * Return absolute column index for a visible column index
490 * int column index in alignment view
491 * @return alignment column index for column
493 public int adjustForHiddenColumns(int column)
496 if (hiddenColumns != null)
498 for (int i = 0; i < hiddenColumns.size(); i++)
500 int[] region = (int[]) hiddenColumns.elementAt(i);
501 if (result >= region[0])
503 result += region[1] - region[0] + 1;
511 * Use this method to find out where a column will appear in the visible
512 * alignment when hidden columns exist. If the column is not visible, then the
513 * left-most visible column will always be returned.
515 * @param hiddenColumn
519 public int findColumnPosition(int hiddenColumn)
521 int result = hiddenColumn;
522 if (hiddenColumns != null)
528 region = (int[]) hiddenColumns.elementAt(index++);
529 if (hiddenColumn > region[1])
531 result -= region[1] + 1 - region[0];
533 } while ((hiddenColumn > region[1]) && (index < hiddenColumns.size()));
534 if (hiddenColumn > region[0] && hiddenColumn < region[1])
536 return region[0] + hiddenColumn - result;
539 return result; // return the shifted position after removing hidden columns.
543 * Use this method to determine where the next hiddenRegion starts
545 public int findHiddenRegionPosition(int hiddenRegion)
548 if (hiddenColumns != null)
554 int[] region = (int[]) hiddenColumns.elementAt(index);
555 if (hiddenRegion == 0)
560 gaps += region[1] + 1 - region[0];
561 result = region[1] + 1;
563 } while (index < hiddenRegion + 1);
572 * THis method returns the rightmost limit of a region of an alignment with
573 * hidden columns. In otherwords, the next hidden column.
578 public int getHiddenBoundaryRight(int alPos)
580 if (hiddenColumns != null)
585 int[] region = (int[]) hiddenColumns.elementAt(index);
586 if (alPos < region[0])
592 } while (index < hiddenColumns.size());
600 * This method returns the leftmost limit of a region of an alignment with
601 * hidden columns. In otherwords, the previous hidden column.
606 public int getHiddenBoundaryLeft(int alPos)
608 if (hiddenColumns != null)
610 int index = hiddenColumns.size() - 1;
613 int[] region = (int[]) hiddenColumns.elementAt(index);
614 if (alPos > region[1])
620 } while (index > -1);
627 public void hideSelectedColumns()
631 int column = ((Integer) getSelected().firstElement()).intValue();
637 public void hideColumns(int start, int end)
639 if (hiddenColumns == null)
641 hiddenColumns = new Vector();
644 boolean added = false;
645 boolean overlap = false;
647 for (int i = 0; i < hiddenColumns.size(); i++)
649 int[] region = (int[]) hiddenColumns.elementAt(i);
650 if (start <= region[1] && end >= region[0])
652 hiddenColumns.removeElementAt(i);
656 else if (end < region[0] && start < region[0])
658 hiddenColumns.insertElementAt(new int[]
667 hideColumns(start, end);
671 hiddenColumns.addElement(new int[]
678 * This method will find a range of selected columns around the column
684 public void hideColumns(int col)
686 // First find out range of columns to hide
687 int min = col, max = col + 1;
688 while (contains(min))
694 while (contains(max))
707 hideColumns(min, max);
710 public void revealAllHiddenColumns()
712 if (hiddenColumns != null)
714 for (int i = 0; i < hiddenColumns.size(); i++)
716 int[] region = (int[]) hiddenColumns.elementAt(i);
717 for (int j = region[0]; j < region[1] + 1; j++)
724 hiddenColumns = null;
727 public void revealHiddenColumns(int res)
729 for (int i = 0; i < hiddenColumns.size(); i++)
731 int[] region = (int[]) hiddenColumns.elementAt(i);
732 if (res == region[0])
734 for (int j = region[0]; j < region[1] + 1; j++)
739 hiddenColumns.removeElement(region);
743 if (hiddenColumns.size() == 0)
745 hiddenColumns = null;
749 public boolean isVisible(int column)
751 if (hiddenColumns != null)
752 for (int i = 0; i < hiddenColumns.size(); i++)
754 int[] region = (int[]) hiddenColumns.elementAt(i);
755 if (column >= region[0] && column <= region[1])
769 public ColumnSelection(ColumnSelection copy)
773 if (copy.selected != null)
775 selected = new Vector();
776 for (int i = 0, j = copy.selected.size(); i < j; i++)
778 selected.addElement(copy.selected.elementAt(i));
781 if (copy.hiddenColumns != null)
783 hiddenColumns = new Vector(copy.hiddenColumns.size());
784 for (int i = 0, j = copy.hiddenColumns.size(); i < j; i++)
787 rh = (int[]) copy.hiddenColumns.elementAt(i);
790 cp = new int[rh.length];
791 System.arraycopy(rh, 0, cp, 0, rh.length);
792 hiddenColumns.addElement(cp);
802 public ColumnSelection()
806 public String[] getVisibleSequenceStrings(int start, int end,
809 int i, iSize = seqs.length;
810 String selection[] = new String[iSize];
811 if (hiddenColumns != null && hiddenColumns.size() > 0)
813 for (i = 0; i < iSize; i++)
815 StringBuffer visibleSeq = new StringBuffer();
816 Vector regions = getHiddenColumns();
818 int blockStart = start, blockEnd = end;
820 int hideStart, hideEnd;
822 for (int j = 0; j < regions.size(); j++)
824 region = (int[]) regions.elementAt(j);
825 hideStart = region[0];
828 if (hideStart < start)
833 blockStart = Math.min(blockStart, hideEnd + 1);
834 blockEnd = Math.min(blockEnd, hideStart);
836 if (blockStart > blockEnd)
841 visibleSeq.append(seqs[i].getSequence(blockStart, blockEnd));
843 blockStart = hideEnd + 1;
847 if (end > blockStart)
849 visibleSeq.append(seqs[i].getSequence(blockStart, end));
852 selection[i] = visibleSeq.toString();
857 for (i = 0; i < iSize; i++)
859 selection[i] = seqs[i].getSequenceAsString(start, end);
867 * return all visible segments between the given start and end boundaries
870 * (first column inclusive from 0)
872 * (last column - not inclusive)
873 * @return int[] {i_start, i_end, ..} where intervals lie in
874 * start<=i_start<=i_end<end
876 public int[] getVisibleContigs(int start, int end)
878 if (hiddenColumns != null && hiddenColumns.size() > 0)
880 Vector visiblecontigs = new Vector();
881 Vector regions = getHiddenColumns();
885 int hideStart, hideEnd;
887 for (int j = 0; vstart < end && j < regions.size(); j++)
889 region = (int[]) regions.elementAt(j);
890 hideStart = region[0];
893 if (hideEnd < vstart)
897 if (hideStart > vstart)
899 visiblecontigs.addElement(new int[]
900 { vstart, hideStart - 1 });
902 vstart = hideEnd + 1;
907 visiblecontigs.addElement(new int[]
908 { vstart, end - 1 });
910 int[] vcontigs = new int[visiblecontigs.size() * 2];
911 for (int i = 0, j = visiblecontigs.size(); i < j; i++)
913 int[] vc = (int[]) visiblecontigs.elementAt(i);
914 visiblecontigs.setElementAt(null, i);
915 vcontigs[i * 2] = vc[0];
916 vcontigs[i * 2 + 1] = vc[1];
918 visiblecontigs.removeAllElements();
929 * delete any columns in alignmentAnnotation that are hidden (including
930 * sequence associated annotation).
932 * @param alignmentAnnotation
934 public void makeVisibleAnnotation(AlignmentAnnotation alignmentAnnotation)
936 makeVisibleAnnotation(-1, -1, alignmentAnnotation);
940 * delete any columns in alignmentAnnotation that are hidden (including
941 * sequence associated annotation).
944 * remove any annotation to the right of this column
946 * remove any annotation to the left of this column
947 * @param alignmentAnnotation
948 * the annotation to operate on
950 public void makeVisibleAnnotation(int start, int end,
951 AlignmentAnnotation alignmentAnnotation)
953 if (alignmentAnnotation.annotations == null)
957 if (start == end && end == -1)
960 end = alignmentAnnotation.annotations.length;
962 if (hiddenColumns != null && hiddenColumns.size() > 0)
964 // then mangle the alignmentAnnotation annotation array
965 Vector annels = new Vector();
966 Annotation[] els = null;
967 Vector regions = getHiddenColumns();
968 int blockStart = start, blockEnd = end;
970 int hideStart, hideEnd, w = 0;
972 for (int j = 0; j < regions.size(); j++)
974 region = (int[]) regions.elementAt(j);
975 hideStart = region[0];
978 if (hideStart < start)
983 blockStart = Math.min(blockStart, hideEnd + 1);
984 blockEnd = Math.min(blockEnd, hideStart);
986 if (blockStart > blockEnd)
991 annels.addElement(els = new Annotation[blockEnd - blockStart]);
992 System.arraycopy(alignmentAnnotation.annotations, blockStart, els,
995 blockStart = hideEnd + 1;
999 if (end > blockStart)
1001 annels.addElement(els = new Annotation[end - blockStart + 1]);
1002 if ((els.length + blockStart) <= alignmentAnnotation.annotations.length)
1004 // copy just the visible segment of the annotation row
1005 System.arraycopy(alignmentAnnotation.annotations, blockStart,
1006 els, 0, els.length);
1010 // copy to the end of the annotation row
1011 System.arraycopy(alignmentAnnotation.annotations, blockStart,
1013 (alignmentAnnotation.annotations.length - blockStart));
1019 Enumeration e = annels.elements();
1020 alignmentAnnotation.annotations = new Annotation[w];
1022 while (e.hasMoreElements())
1024 Annotation[] chnk = (Annotation[]) e.nextElement();
1025 System.arraycopy(chnk, 0, alignmentAnnotation.annotations, w,
1032 alignmentAnnotation.restrict(start, end);
1037 * Invert the column selection from first to end-1. leaves hiddenColumns
1038 * untouched (and unselected)
1043 public void invertColumnSelection(int first, int width)
1045 boolean hasHidden = hiddenColumns != null && hiddenColumns.size() > 0;
1046 for (int i = first; i < width; i++)
1054 if (!hasHidden || isVisible(i))
1063 * add in any unselected columns from the given column selection, excluding
1064 * any that are hidden.
1068 public void addElementsFrom(ColumnSelection colsel)
1070 if (colsel != null && colsel.size() > 0)
1072 Enumeration e = colsel.getSelected().elements();
1073 while (e.hasMoreElements())
1075 Object eo = e.nextElement();
1076 if (hiddenColumns != null && isVisible(((Integer) eo).intValue()))
1078 if (!selected.contains(eo))
1080 selected.addElement(eo);
1088 * set the selected columns the given column selection, excluding any columns
1093 public void setElementsFrom(ColumnSelection colsel)
1095 selected = new Vector();
1096 if (colsel.selected != null && colsel.selected.size() > 0)
1098 if (hiddenColumns != null && hiddenColumns.size() > 0)
1100 // only select visible columns in this columns selection
1101 selected = new Vector();
1102 addElementsFrom(colsel);
1106 // add everything regardless
1107 Enumeration en = colsel.selected.elements();
1108 while (en.hasMoreElements())
1110 selected.addElement(en.nextElement());
1117 * Add gaps into the sequences aligned to profileseq under the given
1122 * - alignment to have gaps inserted into it
1124 * - alignment view where sequence corresponding to profileseq is
1126 * @return new Column selection for new alignment view, with insertions into
1127 * profileseq marked as hidden.
1129 public static ColumnSelection propagateInsertions(SequenceI profileseq,
1130 Alignment al, AlignmentView input)
1134 // return propagateInsertions(profileseq, al, )
1135 char gc = al.getGapCharacter();
1136 Object[] alandcolsel = input.getAlignmentAndColumnSelection(gc);
1137 ColumnSelection nview = (ColumnSelection) alandcolsel[1];
1138 SequenceI origseq = ((SequenceI[]) alandcolsel[0])[profsqpos];
1139 nview.propagateInsertions(profileseq, al, origseq);
1146 * - sequence in al which corresponds to origseq
1148 * - alignment which is to have gaps inserted into it
1150 * - sequence corresponding to profileseq which defines gap map for
1153 public void propagateInsertions(SequenceI profileseq, AlignmentI al,
1156 char gc = al.getGapCharacter();
1157 // recover mapping between sequence's non-gap positions and positions
1159 pruneDeletions(ShiftList.parseMap(origseq.gapMap()));
1160 int[] viscontigs = getVisibleContigs(0, profileseq.getLength());
1163 // input.pruneDeletions(ShiftList.parseMap(((SequenceI[])
1164 // alandcolsel[0])[0].gapMap()))
1165 // add profile to visible contigs
1166 for (int v = 0; v < viscontigs.length; v += 2)
1168 if (viscontigs[v] > spos)
1170 StringBuffer sb = new StringBuffer();
1171 for (int s = 0, ns = viscontigs[v] - spos; s < ns; s++)
1175 for (int s = 0, ns = al.getHeight(); s < ns; s++)
1177 SequenceI sqobj = al.getSequenceAt(s);
1178 if (sqobj != profileseq)
1180 String sq = al.getSequenceAt(s).getSequenceAsString();
1181 if (sq.length() <= spos + offset)
1184 int diff = spos + offset - sq.length() - 1;
1189 while ((diff = spos + offset - sq.length() - 1) > 0)
1192 // + ((diff >= sb.length()) ? sb.toString() : sb
1193 // .substring(0, diff));
1194 if (diff >= sb.length())
1196 sq += sb.toString();
1200 char[] buf = new char[diff];
1201 sb.getChars(0, diff, buf, 0);
1202 sq += buf.toString();
1206 sq += sb.toString();
1210 al.getSequenceAt(s).setSequence(
1211 sq.substring(0, spos + offset) + sb.toString()
1212 + sq.substring(spos + offset));
1216 // offset+=sb.length();
1218 spos = viscontigs[v + 1] + 1;
1220 if ((offset + spos) < profileseq.getLength())
1222 // pad the final region with gaps.
1223 StringBuffer sb = new StringBuffer();
1224 for (int s = 0, ns = profileseq.getLength() - spos - offset; s < ns; s++)
1228 for (int s = 0, ns = al.getHeight(); s < ns; s++)
1230 SequenceI sqobj = al.getSequenceAt(s);
1231 if (sqobj == profileseq)
1235 String sq = sqobj.getSequenceAsString();
1237 int diff = origseq.getLength() - sq.length();
1241 // + ((diff >= sb.length()) ? sb.toString() : sb
1242 // .substring(0, diff));
1243 if (diff >= sb.length())
1245 sq += sb.toString();
1249 char[] buf = new char[diff];
1250 sb.getChars(0, diff, buf, 0);
1251 sq += buf.toString();
1253 diff = origseq.getLength() - sq.length();