2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.4)
\r
3 * Copyright (C) 2008 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
\r
5 * This program is free software; you can redistribute it and/or
\r
6 * modify it under the terms of the GNU General Public License
\r
7 * as published by the Free Software Foundation; either version 2
\r
8 * of the License, or (at your option) any later version.
\r
10 * This program is distributed in the hope that it will be useful,
\r
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 * GNU General Public License for more details.
\r
15 * You should have received a copy of the GNU General Public License
\r
16 * along with this program; if not, write to the Free Software
\r
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
\r
19 package jalview.datamodel;
\r
23 import jalview.util.*;
\r
26 * NOTE: Columns are zero based.
\r
28 public class ColumnSelection
\r
30 Vector selected = new Vector();
\r
32 // Vector of int [] {startCol, endCol}
\r
33 Vector hiddenColumns;
\r
36 * Add a column to the selection
\r
41 public void addElement(int col)
\r
43 Integer column = new Integer(col);
\r
44 if (!selected.contains(column))
\r
46 selected.addElement(column);
\r
51 * clears column selection
\r
55 selected.removeAllElements();
\r
59 * removes col from selection
\r
62 * index of column to be removed
\r
64 public void removeElement(int col)
\r
66 Integer colInt = new Integer(col);
\r
68 if (selected.contains(colInt))
\r
70 selected.removeElement(colInt);
\r
75 * removes a range of columns from the selection
\r
78 * int - first column in range to be removed
\r
82 public void removeElements(int start, int end)
\r
85 for (int i = start; i < end; i++)
\r
87 colInt = new Integer(i);
\r
88 if (selected.contains(colInt))
\r
90 selected.removeElement(colInt);
\r
97 * @return Vector containing selected columns as Integers
\r
99 public Vector getSelected()
\r
107 * index to search for in column selection
\r
109 * @return true if Integer(col) is in selection.
\r
111 public boolean contains(int col)
\r
113 return selected.contains(new Integer(col));
\r
117 * Column number at position i in selection
\r
120 * index into selected columns
\r
122 * @return column number in alignment
\r
124 public int columnAt(int i)
\r
126 return ((Integer) selected.elementAt(i)).intValue();
\r
132 * @return DOCUMENT ME!
\r
136 return selected.size();
\r
140 * rightmost selected column
\r
142 * @return rightmost column in alignment that is selected
\r
144 public int getMax()
\r
148 for (int i = 0; i < selected.size(); i++)
\r
150 if (columnAt(i) > max)
\r
160 * Leftmost column in selection
\r
162 * @return column index of leftmost column in selection
\r
164 public int getMin()
\r
166 int min = 1000000000;
\r
168 for (int i = 0; i < selected.size(); i++)
\r
170 if (columnAt(i) < min)
\r
180 * propagate shift in alignment columns to column selection
\r
183 * beginning of edit
\r
185 * shift in edit (+ve for removal, or -ve for inserts)
\r
187 public Vector compensateForEdit(int start, int change)
\r
189 Vector deletedHiddenColumns = null;
\r
190 for (int i = 0; i < size(); i++)
\r
192 int temp = columnAt(i);
\r
196 selected.setElementAt(new Integer(temp - change), i);
\r
200 if (hiddenColumns != null)
\r
202 deletedHiddenColumns = new Vector();
\r
203 int hSize = hiddenColumns.size();
\r
204 for (int i = 0; i < hSize; i++)
\r
206 int[] region = (int[]) hiddenColumns.elementAt(i);
\r
207 if (region[0] > start && start + change > region[1])
\r
209 deletedHiddenColumns.addElement(hiddenColumns.elementAt(i));
\r
211 hiddenColumns.removeElementAt(i);
\r
217 if (region[0] > start)
\r
219 region[0] -= change;
\r
220 region[1] -= change;
\r
230 this.revealHiddenColumns(0);
\r
233 return deletedHiddenColumns;
\r
237 * propagate shift in alignment columns to column selection special version of
\r
238 * compensateForEdit - allowing for edits within hidden regions
\r
241 * beginning of edit
\r
243 * shift in edit (+ve for removal, or -ve for inserts)
\r
245 private void compensateForDelEdits(int start, int change)
\r
247 for (int i = 0; i < size(); i++)
\r
249 int temp = columnAt(i);
\r
253 selected.setElementAt(new Integer(temp - change), i);
\r
257 if (hiddenColumns != null)
\r
259 for (int i = 0; i < hiddenColumns.size(); i++)
\r
261 int[] region = (int[]) hiddenColumns.elementAt(i);
\r
262 if (region[0] >= start)
\r
264 region[0] -= change;
\r
266 if (region[1] >= start)
\r
268 region[1] -= change;
\r
270 if (region[1] < region[0])
\r
272 hiddenColumns.removeElementAt(i--);
\r
288 * Adjust hidden column boundaries based on a series of column additions or
\r
289 * deletions in visible regions.
\r
291 * @param shiftrecord
\r
294 public ShiftList compensateForEdits(ShiftList shiftrecord)
\r
296 if (shiftrecord != null)
\r
298 Vector shifts = shiftrecord.shifts;
\r
299 if (shifts != null && shifts.size() > 0)
\r
302 for (int i = 0, j = shifts.size(); i < j; i++)
\r
304 int[] sh = (int[]) shifts.elementAt(i);
\r
305 // compensateForEdit(shifted+sh[0], sh[1]);
\r
306 compensateForDelEdits(shifted + sh[0], sh[1]);
\r
310 return shiftrecord.getInverse();
\r
316 * removes intersection of position,length ranges in deletions from the
\r
317 * start,end regions marked in intervals.
\r
323 private boolean pruneIntervalVector(Vector deletions, Vector intervals)
\r
325 boolean pruned = false;
\r
326 int i = 0, j = intervals.size() - 1, s = 0, t = deletions.size() - 1;
\r
327 int hr[] = (int[]) intervals.elementAt(i);
\r
328 int sr[] = (int[]) deletions.elementAt(s);
\r
329 while (i <= j && s <= t)
\r
331 boolean trailinghn = hr[1] >= sr[0];
\r
336 hr = (int[]) intervals.elementAt(++i);
\r
344 int endshift = sr[0] + sr[1]; // deletion ranges - -ve means an insert
\r
345 if (endshift < hr[0] || endshift < sr[0])
\r
346 { // leadinghc disjoint or not a deletion
\r
349 sr = (int[]) deletions.elementAt(++s);
\r
357 boolean leadinghn = hr[0] >= sr[0];
\r
358 boolean leadinghc = hr[0] < endshift;
\r
359 boolean trailinghc = hr[1] < endshift;
\r
363 { // deleted hidden region.
\r
364 intervals.removeElementAt(i);
\r
369 hr = (int[]) intervals.elementAt(i);
\r
375 hr[0] = endshift; // clip c terminal region
\r
376 leadinghn = !leadinghn;
\r
392 // sr contained in hr
\r
395 sr = (int[]) deletions.elementAt(++s);
\r
405 return pruned; // true if any interval was removed or modified by
\r
409 private boolean pruneColumnList(Vector deletion, Vector list)
\r
411 int s = 0, t = deletion.size();
\r
412 int[] sr = (int[]) list.elementAt(s++);
\r
413 boolean pruned = false;
\r
414 int i = 0, j = list.size();
\r
415 while (i < j && s <= t)
\r
417 int c = ((Integer) list.elementAt(i++)).intValue();
\r
420 if (sr[1] + sr[0] >= c)
\r
421 { // sr[1] -ve means inseriton.
\r
422 list.removeElementAt(--i);
\r
429 sr = (int[]) deletion.elementAt(s);
\r
439 * remove any hiddenColumns or selected columns and shift remaining based on a
\r
440 * series of position, range deletions.
\r
444 public void pruneDeletions(ShiftList deletions)
\r
446 if (deletions != null)
\r
448 Vector shifts = deletions.shifts;
\r
449 if (shifts != null && shifts.size() > 0)
\r
451 // delete any intervals intersecting.
\r
452 if (hiddenColumns != null)
\r
454 pruneIntervalVector(shifts, hiddenColumns);
\r
455 if (hiddenColumns != null && hiddenColumns.size() == 0)
\r
457 hiddenColumns = null;
\r
460 if (selected != null && selected.size() > 0)
\r
462 pruneColumnList(shifts, selected);
\r
463 if (selected != null && selected.size() == 0)
\r
468 // and shift the rest.
\r
469 this.compensateForEdits(deletions);
\r
475 * This Method is used to return all the HiddenColumn regions less than the
\r
482 public Vector getHiddenColumns()
\r
484 return hiddenColumns;
\r
488 * Return absolute column index for a visible column index
\r
491 * int column index in alignment view
\r
492 * @return alignment column index for column
\r
494 public int adjustForHiddenColumns(int column)
\r
496 int result = column;
\r
497 if (hiddenColumns != null)
\r
499 for (int i = 0; i < hiddenColumns.size(); i++)
\r
501 int[] region = (int[]) hiddenColumns.elementAt(i);
\r
502 if (result >= region[0])
\r
504 result += region[1] - region[0] + 1;
\r
512 * Use this method to find out where a visible column is in the alignment when
\r
513 * hidden columns exist
\r
515 * @param hiddenColumn
\r
519 public int findColumnPosition(int hiddenColumn)
\r
521 int result = hiddenColumn;
\r
522 if (hiddenColumns != null)
\r
528 int[] region = (int[]) hiddenColumns.elementAt(index);
\r
529 if (hiddenColumn > region[1])
\r
531 result -= region[1] + 1 - region[0];
\r
534 } while (index < hiddenColumns.size());
\r
543 * Use this method to determine where the next hiddenRegion starts
\r
545 public int findHiddenRegionPosition(int hiddenRegion)
\r
548 if (hiddenColumns != null)
\r
554 int[] region = (int[]) hiddenColumns.elementAt(index);
\r
555 if (hiddenRegion == 0)
\r
560 gaps += region[1] + 1 - region[0];
\r
561 result = region[1] + 1;
\r
563 } while (index < hiddenRegion + 1);
\r
572 * THis method returns the rightmost limit of a region of an alignment with
\r
573 * hidden columns. In otherwords, the next hidden column.
\r
578 public int getHiddenBoundaryRight(int alPos)
\r
580 if (hiddenColumns != null)
\r
585 int[] region = (int[]) hiddenColumns.elementAt(index);
\r
586 if (alPos < region[0])
\r
592 } while (index < hiddenColumns.size());
\r
600 * This method returns the leftmost limit of a region of an alignment with
\r
601 * hidden columns. In otherwords, the previous hidden column.
\r
606 public int getHiddenBoundaryLeft(int alPos)
\r
608 if (hiddenColumns != null)
\r
610 int index = hiddenColumns.size() - 1;
\r
613 int[] region = (int[]) hiddenColumns.elementAt(index);
\r
614 if (alPos > region[1])
\r
620 } while (index > -1);
\r
627 public void hideSelectedColumns()
\r
631 int column = ((Integer) getSelected().firstElement()).intValue();
\r
632 hideColumns(column);
\r
637 public void hideColumns(int start, int end)
\r
639 if (hiddenColumns == null)
\r
641 hiddenColumns = new Vector();
\r
644 boolean added = false;
\r
645 boolean overlap = false;
\r
647 for (int i = 0; i < hiddenColumns.size(); i++)
\r
649 int[] region = (int[]) hiddenColumns.elementAt(i);
\r
650 if (start <= region[1] && end >= region[0])
\r
652 hiddenColumns.removeElementAt(i);
\r
656 else if (end < region[0] && start < region[0])
\r
658 hiddenColumns.insertElementAt(new int[]
\r
659 { start, end }, i);
\r
667 hideColumns(start, end);
\r
671 hiddenColumns.addElement(new int[]
\r
678 * This method will find a range of selected columns around the column
\r
684 public void hideColumns(int col)
\r
686 // First find out range of columns to hide
\r
687 int min = col, max = col + 1;
\r
688 while (contains(min))
\r
690 removeElement(min);
\r
694 while (contains(max))
\r
696 removeElement(max);
\r
707 hideColumns(min, max);
\r
710 public void revealAllHiddenColumns()
\r
712 if (hiddenColumns != null)
\r
714 for (int i = 0; i < hiddenColumns.size(); i++)
\r
716 int[] region = (int[]) hiddenColumns.elementAt(i);
\r
717 for (int j = region[0]; j < region[1] + 1; j++)
\r
724 hiddenColumns = null;
\r
727 public void revealHiddenColumns(int res)
\r
729 for (int i = 0; i < hiddenColumns.size(); i++)
\r
731 int[] region = (int[]) hiddenColumns.elementAt(i);
\r
732 if (res == region[0])
\r
734 for (int j = region[0]; j < region[1] + 1; j++)
\r
739 hiddenColumns.removeElement(region);
\r
743 if (hiddenColumns.size() == 0)
\r
745 hiddenColumns = null;
\r
749 public boolean isVisible(int column)
\r
751 if (hiddenColumns != null)
\r
752 for (int i = 0; i < hiddenColumns.size(); i++)
\r
754 int[] region = (int[]) hiddenColumns.elementAt(i);
\r
755 if (column >= region[0] && column <= region[1])
\r
769 public ColumnSelection(ColumnSelection copy)
\r
773 if (copy.selected != null)
\r
775 selected = new Vector();
\r
776 for (int i = 0, j = copy.selected.size(); i < j; i++)
\r
778 selected.addElement(copy.selected.elementAt(i));
\r
781 if (copy.hiddenColumns != null)
\r
783 hiddenColumns = new Vector(copy.hiddenColumns.size());
\r
784 for (int i = 0, j = copy.hiddenColumns.size(); i < j; i++)
\r
787 rh = (int[]) copy.hiddenColumns.elementAt(i);
\r
790 cp = new int[rh.length];
\r
791 System.arraycopy(rh, 0, cp, 0, rh.length);
\r
792 hiddenColumns.addElement(cp);
\r
802 public ColumnSelection()
\r
806 public String[] getVisibleSequenceStrings(int start, int end,
\r
809 int i, iSize = seqs.length;
\r
810 String selection[] = new String[iSize];
\r
811 if (hiddenColumns != null && hiddenColumns.size() > 0)
\r
813 for (i = 0; i < iSize; i++)
\r
815 StringBuffer visibleSeq = new StringBuffer();
\r
816 Vector regions = getHiddenColumns();
\r
818 int blockStart = start, blockEnd = end;
\r
820 int hideStart, hideEnd;
\r
822 for (int j = 0; j < regions.size(); j++)
\r
824 region = (int[]) regions.elementAt(j);
\r
825 hideStart = region[0];
\r
826 hideEnd = region[1];
\r
828 if (hideStart < start)
\r
833 blockStart = Math.min(blockStart, hideEnd + 1);
\r
834 blockEnd = Math.min(blockEnd, hideStart);
\r
836 if (blockStart > blockEnd)
\r
841 visibleSeq.append(seqs[i].getSequence(blockStart, blockEnd));
\r
843 blockStart = hideEnd + 1;
\r
847 if (end > blockStart)
\r
849 visibleSeq.append(seqs[i].getSequence(blockStart, end));
\r
852 selection[i] = visibleSeq.toString();
\r
857 for (i = 0; i < iSize; i++)
\r
859 selection[i] = seqs[i].getSequenceAsString(start, end);
\r
867 * return all visible segments between the given start and end boundaries
\r
870 * (first column inclusive from 0)
\r
872 * (last column - not inclusive)
\r
873 * @return int[] {i_start, i_end, ..} where intervals lie in start<=i_start<=i_end<end
\r
875 public int[] getVisibleContigs(int start, int end)
\r
877 if (hiddenColumns != null && hiddenColumns.size() > 0)
\r
879 Vector visiblecontigs = new Vector();
\r
880 Vector regions = getHiddenColumns();
\r
882 int vstart = start;
\r
884 int hideStart, hideEnd;
\r
886 for (int j = 0; vstart < end && j < regions.size(); j++)
\r
888 region = (int[]) regions.elementAt(j);
\r
889 hideStart = region[0];
\r
890 hideEnd = region[1];
\r
892 if (hideEnd < vstart)
\r
896 if (hideStart > vstart)
\r
898 visiblecontigs.addElement(new int[]
\r
899 { vstart, hideStart - 1 });
\r
901 vstart = hideEnd + 1;
\r
906 visiblecontigs.addElement(new int[]
\r
907 { vstart, end - 1 });
\r
909 int[] vcontigs = new int[visiblecontigs.size() * 2];
\r
910 for (int i = 0, j = visiblecontigs.size(); i < j; i++)
\r
912 int[] vc = (int[]) visiblecontigs.elementAt(i);
\r
913 visiblecontigs.setElementAt(null, i);
\r
914 vcontigs[i * 2] = vc[0];
\r
915 vcontigs[i * 2 + 1] = vc[1];
\r
917 visiblecontigs.removeAllElements();
\r
923 { start, end - 1 };
\r
928 * delete any columns in alignmentAnnotation that are hidden (including
\r
929 * sequence associated annotation).
\r
931 * @param alignmentAnnotation
\r
933 public void makeVisibleAnnotation(AlignmentAnnotation alignmentAnnotation)
\r
935 makeVisibleAnnotation(-1, -1, alignmentAnnotation);
\r
939 * delete any columns in alignmentAnnotation that are hidden (including
\r
940 * sequence associated annotation).
\r
943 * remove any annotation to the right of this column
\r
945 * remove any annotation to the left of this column
\r
946 * @param alignmentAnnotation
\r
947 * the annotation to operate on
\r
949 public void makeVisibleAnnotation(int start, int end,
\r
950 AlignmentAnnotation alignmentAnnotation)
\r
952 if (alignmentAnnotation.annotations == null)
\r
956 if (start == end && end == -1)
\r
959 end = alignmentAnnotation.annotations.length;
\r
961 if (hiddenColumns != null && hiddenColumns.size() > 0)
\r
963 // then mangle the alignmentAnnotation annotation array
\r
964 Vector annels = new Vector();
\r
965 Annotation[] els = null;
\r
966 Vector regions = getHiddenColumns();
\r
967 int blockStart = start, blockEnd = end;
\r
969 int hideStart, hideEnd, w = 0;
\r
971 for (int j = 0; j < regions.size(); j++)
\r
973 region = (int[]) regions.elementAt(j);
\r
974 hideStart = region[0];
\r
975 hideEnd = region[1];
\r
977 if (hideStart < start)
\r
982 blockStart = Math.min(blockStart, hideEnd + 1);
\r
983 blockEnd = Math.min(blockEnd, hideStart);
\r
985 if (blockStart > blockEnd)
\r
990 annels.addElement(els = new Annotation[blockEnd - blockStart]);
\r
991 System.arraycopy(alignmentAnnotation.annotations, blockStart, els,
\r
994 blockStart = hideEnd + 1;
\r
998 if (end > blockStart)
\r
1000 annels.addElement(els = new Annotation[end - blockStart + 1]);
\r
1001 if ((els.length+blockStart)<= alignmentAnnotation.annotations.length)
\r
1003 // copy just the visible segment of the annotation row
\r
1004 System.arraycopy(alignmentAnnotation.annotations, blockStart,
\r
1005 els, 0, els.length);
\r
1009 // copy to the end of the annotation row
\r
1010 System.arraycopy(alignmentAnnotation.annotations, blockStart,
\r
1011 els, 0, (alignmentAnnotation.annotations.length - blockStart));
\r
1017 Enumeration e = annels.elements();
\r
1018 alignmentAnnotation.annotations = new Annotation[w];
\r
1020 while (e.hasMoreElements())
\r
1022 Annotation[] chnk = (Annotation[]) e.nextElement();
\r
1023 System.arraycopy(chnk, 0, alignmentAnnotation.annotations, w,
\r
1030 alignmentAnnotation.restrict(start, end);
\r
1035 * Invert the column selection from first to end-1. leaves hiddenColumns
\r
1036 * untouched (and unselected)
\r
1041 public void invertColumnSelection(int first, int width)
\r
1043 boolean hasHidden = hiddenColumns != null && hiddenColumns.size() > 0;
\r
1044 for (int i = first; i < width; i++)
\r
1052 if (!hasHidden || isVisible(i))
\r