make 1.1 compatible
[jalview.git] / src / jalview / datamodel / ColumnSelection.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer\r
3  * Copyright (C) 2006 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
4  *\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
9  *\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
14  *\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
18  */\r
19 package jalview.datamodel;\r
20 \r
21 import jalview.util.ShiftList;\r
22 \r
23 import java.util.*;\r
24 \r
25 /**\r
26  * NOTE: Columns are zero based.\r
27  */\r
28 public class ColumnSelection\r
29 {\r
30     Vector selected = new Vector();\r
31 \r
32     //Vector of int [] {startCol, endCol}\r
33     Vector hiddenColumns;\r
34 \r
35     /**\r
36      * Add a column to the selection\r
37      *\r
38      * @param col index of column\r
39      */\r
40     public void addElement(int col)\r
41     {\r
42         Integer column = new Integer(col);\r
43         if (!selected.contains(column))\r
44         {\r
45             selected.addElement(column);\r
46         }\r
47     }\r
48 \r
49     /**\r
50      * clears column selection\r
51      */\r
52     public void clear()\r
53     {\r
54         selected.removeAllElements();\r
55     }\r
56 \r
57     /**\r
58      * removes col from selection\r
59      *\r
60      * @param col index of column to be removed\r
61      */\r
62     public void removeElement(int col)\r
63     {\r
64         Integer colInt = new Integer(col);\r
65 \r
66         if (selected.contains(colInt))\r
67         {\r
68             selected.removeElement(colInt);\r
69         }\r
70     }\r
71 \r
72     /**\r
73      * removes a range of columns from the selection\r
74      * @param start int - first column in range to be removed\r
75      * @param end int - last col\r
76      */\r
77     public void removeElements(int start, int end)\r
78     {\r
79       Integer colInt;\r
80       for(int i=start; i<end; i++)\r
81       {\r
82         colInt = new Integer(i);\r
83         if (selected.contains(colInt))\r
84         {\r
85             selected.removeElement(colInt);\r
86         }\r
87       }\r
88     }\r
89     /**\r
90      *\r
91      * @return Vector containing selected columns as Integers\r
92      */\r
93     public Vector getSelected()\r
94     {\r
95       return selected;\r
96     }\r
97 \r
98     /**\r
99      *\r
100      * @param col index to search for in column selection\r
101      *\r
102      * @return true if Integer(col) is in selection.\r
103      */\r
104     public boolean contains(int col)\r
105     {\r
106         return selected.contains(new Integer(col));\r
107     }\r
108 \r
109     /**\r
110      * DOCUMENT ME!\r
111      *\r
112      * @param i DOCUMENT ME!\r
113      *\r
114      * @return DOCUMENT ME!\r
115      */\r
116     public int columnAt(int i)\r
117     {\r
118         return ((Integer) selected.elementAt(i)).intValue();\r
119     }\r
120 \r
121     /**\r
122      * DOCUMENT ME!\r
123      *\r
124      * @return DOCUMENT ME!\r
125      */\r
126     public int size()\r
127     {\r
128         return selected.size();\r
129     }\r
130 \r
131     /**\r
132      * DOCUMENT ME!\r
133      *\r
134      * @return DOCUMENT ME!\r
135      */\r
136     public int getMax()\r
137     {\r
138         int max = -1;\r
139 \r
140         for (int i = 0; i < selected.size(); i++)\r
141         {\r
142             if (columnAt(i) > max)\r
143             {\r
144                 max = columnAt(i);\r
145             }\r
146         }\r
147 \r
148         return max;\r
149     }\r
150 \r
151     /**\r
152      * DOCUMENT ME!\r
153      *\r
154      * @return DOCUMENT ME!\r
155      */\r
156     public int getMin()\r
157     {\r
158         int min = 1000000000;\r
159 \r
160         for (int i = 0; i < selected.size(); i++)\r
161         {\r
162             if (columnAt(i) < min)\r
163             {\r
164                 min = columnAt(i);\r
165             }\r
166         }\r
167 \r
168         return min;\r
169     }\r
170 \r
171 \r
172     /**\r
173      * propagate shift in alignment columns to column selection\r
174      *\r
175      * @param start beginning of edit\r
176      * @param left shift in edit (+ve for removal, or -ve for inserts)\r
177      */\r
178     public void compensateForEdit(int start, int change)\r
179     {\r
180         for (int i = 0; i < size(); i++)\r
181         {\r
182             int temp = columnAt(i);\r
183 \r
184             if (temp >= start)\r
185             {\r
186                 selected.setElementAt(new Integer(temp - change), i);\r
187             }\r
188         }\r
189 \r
190         if(hiddenColumns!=null)\r
191         {\r
192           for(int i=0; i<hiddenColumns.size(); i++)\r
193           {\r
194             int[] region = (int[]) hiddenColumns.elementAt(i);\r
195             if(region[0] > start)\r
196             {\r
197               region[0] -= change;\r
198               region[1] -= change;\r
199             }\r
200             if(region[0]<0)\r
201               region[0] = 0;\r
202             if(region[1] <0)\r
203              region[1] = 0;\r
204           }\r
205         }\r
206     }\r
207     /**\r
208      * propagate shift in alignment columns to column selection\r
209      * special version of compensateForEdit - allowing for edits within hidden regions\r
210      * @param start beginning of edit\r
211      * @param left shift in edit (+ve for removal, or -ve for inserts)\r
212      */\r
213     private void compensateForDelEdits(int start, int change)\r
214     {\r
215         for (int i = 0; i < size(); i++)\r
216         {\r
217             int temp = columnAt(i);\r
218 \r
219             if (temp >= start)\r
220             {\r
221                 selected.setElementAt(new Integer(temp - change), i);\r
222             }\r
223         }\r
224 \r
225         if(hiddenColumns!=null)\r
226         {\r
227           for(int i=0; i<hiddenColumns.size(); i++)\r
228           {\r
229             int[] region = (int[]) hiddenColumns.elementAt(i);\r
230             if(region[0] >= start)\r
231             {\r
232               region[0] -= change;\r
233             }\r
234             if (region[1]>= start) {\r
235               region[1] -=change;\r
236             }\r
237             if (region[1]<region[0]) {\r
238               hiddenColumns.removeElementAt(i--);\r
239             }\r
240 \r
241             if(region[0]<0)\r
242               region[0] = 0;\r
243             if(region[1] <0)\r
244              region[1] = 0;\r
245           }\r
246         }\r
247     }\r
248     /**\r
249      * Adjust hidden column boundaries based on a series of column\r
250      * additions or deletions in visible regions.\r
251      * @param shiftrecord\r
252      * @return\r
253      */\r
254     public ShiftList compensateForEdits(ShiftList shiftrecord) {\r
255       if (shiftrecord!=null) {\r
256         Vector shifts = shiftrecord.shifts;\r
257         if (shifts!=null && shifts.size()>0) {\r
258           int shifted=0;\r
259           for (int i=0,j=shifts.size(); i<j; i++) {\r
260             int[] sh = (int[]) shifts.elementAt(i);\r
261             //compensateForEdit(shifted+sh[0], sh[1]);\r
262             compensateForDelEdits(shifted+sh[0], sh[1]);\r
263             shifted-=sh[1];\r
264           }\r
265         }\r
266         return shiftrecord.getInverse();\r
267       }\r
268       return null;\r
269     }\r
270     /**\r
271      * removes intersection of position,length ranges in deletions\r
272      * from the start,end regions marked in intervals.\r
273      * @param deletions\r
274      * @param intervals\r
275      * @return\r
276      */\r
277     private boolean pruneIntervalVector(Vector deletions, Vector intervals) {\r
278       boolean pruned=false;\r
279       int i=0,j=intervals.size()-1, s=0, t=deletions.size()-1;\r
280       int hr[]=(int[]) intervals.elementAt(i);\r
281       int sr[]=(int[]) deletions.elementAt(s);\r
282       while (i<=j && s<=t) {\r
283         boolean trailinghn=hr[1]>=sr[0];\r
284         if (!trailinghn) {\r
285           if (i<j)\r
286             hr=(int[]) intervals.elementAt(++i);\r
287           else\r
288             i++;\r
289           continue;\r
290         }\r
291         int endshift=sr[0]+sr[1]; // deletion ranges - -ve means an insert\r
292         if (endshift<hr[0] || endshift<sr[0]) { // leadinghc disjoint or not a deletion\r
293           if (s<t)\r
294             sr=(int[]) deletions.elementAt(++s);\r
295           else\r
296             s++;\r
297           continue;\r
298         }\r
299         boolean leadinghn=hr[0]>=sr[0];\r
300         boolean leadinghc=hr[0]<endshift;\r
301         boolean trailinghc=hr[1]<endshift;\r
302         if (leadinghn) {\r
303           if (trailinghc) {// deleted hidden region.\r
304             intervals.removeElementAt(i);\r
305             pruned=true;\r
306             j--;\r
307             if (i<=j)\r
308               hr=(int[]) intervals.elementAt(i);\r
309             continue;\r
310           }\r
311           if (leadinghc) {\r
312             hr[0]=endshift; // clip c terminal region\r
313             leadinghn=!leadinghn;\r
314             pruned=true;\r
315           }\r
316         }\r
317         if (!leadinghn) {\r
318           if (trailinghc) {\r
319             if (trailinghn) {\r
320               hr[1]=sr[0]-1;\r
321               pruned=true;\r
322             }\r
323           } else {\r
324             // sr contained in hr\r
325             if (s<t)\r
326               sr=(int[]) deletions.elementAt(++s);\r
327             else\r
328               s++;\r
329             continue;\r
330           }\r
331         }\r
332       }\r
333       return pruned; // true if any interval was removed or modified by operations.\r
334     }\r
335     private boolean pruneColumnList(Vector deletion, Vector list) {\r
336       int s=0,t=deletion.size();\r
337       int[] sr=(int[])list.elementAt(s++);\r
338       boolean pruned=false;\r
339       int i=0, j=list.size();\r
340       while (i<j && s<=t) {\r
341         int c=((Integer)list.elementAt(i++)).intValue();\r
342         if (sr[0]<=c) {\r
343           if (sr[1]+sr[0]>=c) { // sr[1] -ve means inseriton.\r
344             list.removeElementAt(--i);\r
345             j--;\r
346           } else {\r
347             if (s<t)\r
348               sr = (int[])deletion.elementAt(s);\r
349             s++;\r
350           }\r
351         }\r
352       }\r
353       return pruned;\r
354     }\r
355     /**\r
356      * remove any hiddenColumns or selected columns and shift remaining\r
357      * based on a series of position, range deletions.\r
358      * @param deletions\r
359      */\r
360     public void pruneDeletions(ShiftList deletions) {\r
361       if (deletions!=null) {\r
362         Vector shifts=deletions.shifts;\r
363         if (shifts!=null && shifts.size()>0) {\r
364           // delete any intervals intersecting.\r
365           if (hiddenColumns!=null) {\r
366             pruneIntervalVector(shifts, hiddenColumns);\r
367             if (hiddenColumns!=null && hiddenColumns.size()==0) {\r
368               hiddenColumns=null;\r
369             }\r
370           }\r
371           if (selected!=null && selected.size()>0) {\r
372             pruneColumnList(shifts, selected);\r
373             if (selected!=null && selected.size()==0)\r
374               selected=null;\r
375           }\r
376           // and shift the rest.\r
377           this.compensateForEdits(deletions);\r
378         }\r
379       }\r
380     }\r
381     /**\r
382      * This Method is used to return all the HiddenColumn regions\r
383      * less than the given index.\r
384      * @param end int\r
385      * @return Vector\r
386      */\r
387     public Vector getHiddenColumns()\r
388     {\r
389       return hiddenColumns;\r
390     }\r
391     /**\r
392      * Return absolute column index for a visible column index\r
393      * @param column int column index in alignment view\r
394      * @return alignment column index for column\r
395      */\r
396     public int adjustForHiddenColumns(int column)\r
397     {\r
398       int result = column;\r
399       if (hiddenColumns != null)\r
400       {\r
401         for (int i = 0; i < hiddenColumns.size(); i++)\r
402         {\r
403           int[] region = (int[]) hiddenColumns.elementAt(i);\r
404           if (result >= region[0])\r
405           {\r
406             result += region[1] - region[0] + 1;\r
407           }\r
408         }\r
409       }\r
410       return result;\r
411     }\r
412 \r
413     /**\r
414      * Use this method to find out where a visible column is in the alignment\r
415      * when hidden columns exist\r
416      * @param hiddenColumn int\r
417      * @return int\r
418      */\r
419     public int findColumnPosition(int hiddenColumn)\r
420     {\r
421       int result = hiddenColumn;\r
422       if (hiddenColumns != null)\r
423       {\r
424         int index = 0;\r
425         int gaps = 0;\r
426         do\r
427         {\r
428           int[] region = (int[]) hiddenColumns.elementAt(index);\r
429           if (hiddenColumn > region[1])\r
430           {\r
431             result -= region[1]+1-region[0];\r
432           }\r
433           index++;\r
434         }\r
435         while (index < hiddenColumns.size());\r
436 \r
437         result -= gaps;\r
438       }\r
439 \r
440       return result;\r
441     }\r
442 \r
443     /**\r
444      * Use this method to determine where the next hiddenRegion starts\r
445     */\r
446     public int findHiddenRegionPosition(int hiddenRegion)\r
447     {\r
448       int result = 0;\r
449       if (hiddenColumns != null)\r
450       {\r
451         int index = 0;\r
452         int gaps = 0;\r
453         do\r
454         {\r
455           int[] region = (int[]) hiddenColumns.elementAt(index);\r
456           if(hiddenRegion==0)\r
457           {\r
458             return region[0];\r
459           }\r
460 \r
461             gaps +=  region[1] +1 - region[0];\r
462             result = region[1] +1;\r
463             index++;\r
464         }\r
465         while(index < hiddenRegion+1);\r
466 \r
467         result -= gaps;\r
468       }\r
469 \r
470       return result;\r
471     }\r
472 \r
473     /**\r
474      * THis method returns the rightmost limit of a\r
475      * region of an alignment with hidden columns.\r
476      * In otherwords, the next hidden column.\r
477      * @param index int\r
478      */\r
479     public int getHiddenBoundaryRight(int alPos)\r
480     {\r
481       if (hiddenColumns != null)\r
482       {\r
483         int index = 0;\r
484         do\r
485         {\r
486           int[] region = (int[]) hiddenColumns.elementAt(index);\r
487           if(alPos < region[0])\r
488             return region[0];\r
489 \r
490           index++;\r
491         }\r
492         while(index < hiddenColumns.size());\r
493       }\r
494 \r
495       return alPos;\r
496 \r
497     }\r
498     /**\r
499      * THis method returns the rightmost limit of a\r
500      * region of an alignment with hidden columns.\r
501      * In otherwords, the next hidden column.\r
502      * @param index int\r
503      */\r
504     public int getHiddenBoundaryLeft(int alPos)\r
505     {\r
506       if (hiddenColumns != null)\r
507       {\r
508         int index = hiddenColumns.size()-1;\r
509         do\r
510         {\r
511           int[] region = (int[]) hiddenColumns.elementAt(index);\r
512           if(alPos > region[1])\r
513             return region[1];\r
514 \r
515           index--;\r
516         }\r
517         while(index >-1);\r
518       }\r
519 \r
520       return alPos;\r
521 \r
522     }\r
523 \r
524     public void hideSelectedColumns()\r
525     {\r
526       while (size() > 0)\r
527       {\r
528         int column = ( (Integer) getSelected().firstElement()).intValue();\r
529         hideColumns(column);\r
530       }\r
531 \r
532     }\r
533 \r
534     public void hideColumns(int start, int end)\r
535     {\r
536       if(hiddenColumns==null)\r
537         hiddenColumns = new Vector();\r
538 \r
539       boolean added = false;\r
540       boolean overlap = false;\r
541 \r
542       for (int i = 0; i < hiddenColumns.size(); i++)\r
543       {\r
544         int[] region = (int[]) hiddenColumns.elementAt(i);\r
545         if ( start<=region[1] && end>=region[0])\r
546         {\r
547           hiddenColumns.removeElementAt(i);\r
548           overlap = true;\r
549           break;\r
550         }\r
551         else if (end < region[0] && start < region[0])\r
552         {\r
553           hiddenColumns.insertElementAt(new int[]\r
554                                         {start, end}, i);\r
555           added = true;\r
556           break;\r
557         }\r
558       }\r
559 \r
560       if(overlap)\r
561       {\r
562          hideColumns(start, end);\r
563       }\r
564       else if (!added)\r
565         hiddenColumns.addElement(new int[] {start, end});\r
566 \r
567     }\r
568 \r
569     /**\r
570      * This method will find a range of selected columns\r
571      * around the column specified\r
572      * @param res int\r
573      */\r
574     public void hideColumns(int col)\r
575     {\r
576       // First find out range of columns to hide\r
577       int min = col, max = col+1;\r
578       while( contains(min) )\r
579       {  removeElement(min); min --;  }\r
580 \r
581       while( contains(max) )\r
582       { removeElement(max);  max ++;  }\r
583 \r
584       min++; max--;\r
585 \r
586       hideColumns(min, max);\r
587     }\r
588 \r
589     public void revealAllHiddenColumns()\r
590     {\r
591       if(hiddenColumns!=null)\r
592       {\r
593         for (int i = 0; i < hiddenColumns.size(); i++)\r
594         {\r
595           int[] region = (int[]) hiddenColumns.elementAt(i);\r
596           for (int j = region[0]; j < region[1]+1; j++)\r
597           {\r
598             addElement(j);\r
599           }\r
600         }\r
601       }\r
602 \r
603       hiddenColumns = null;\r
604     }\r
605 \r
606     public void revealHiddenColumns(int res)\r
607     {\r
608       for(int i=0; i<hiddenColumns.size(); i++)\r
609       {\r
610         int [] region = (int[])hiddenColumns.elementAt(i);\r
611         if( res == region[0])\r
612         {\r
613           for (int j = region[0]; j < region[1]+1; j++)\r
614           {\r
615             addElement(j);\r
616           }\r
617 \r
618           hiddenColumns.removeElement(region);\r
619           break;\r
620         }\r
621       }\r
622       if(hiddenColumns.size()==0)\r
623         hiddenColumns = null;\r
624     }\r
625 \r
626     public boolean isVisible(int column)\r
627     {\r
628       for(int i=0; i<hiddenColumns.size(); i++)\r
629       {\r
630         int [] region = (int[])hiddenColumns.elementAt(i);\r
631         if( column >= region[0] && column <= region[1])\r
632         {\r
633           return false;\r
634         }\r
635       }\r
636       return true;\r
637     }\r
638     /**\r
639      * Copy constructor\r
640      * @param copy\r
641      */\r
642     public ColumnSelection(ColumnSelection copy) {\r
643       if (copy!=null) {\r
644         if (copy.selected!=null) {\r
645           selected = new Vector();\r
646           for (int i=0,j=copy.selected.size(); i<j; i++) {\r
647             selected.setElementAt( ((Integer) copy.selected.elementAt(i)), i);\r
648           }\r
649         }\r
650         if (copy.hiddenColumns!=null) {\r
651           hiddenColumns=new Vector();\r
652           for (int i=0,j=copy.hiddenColumns.size(); i<j; i++) {\r
653             int[] rh,cp;\r
654             rh = (int[])copy.hiddenColumns.elementAt(i);\r
655             if (rh!=null) {\r
656               cp = new int[rh.length];\r
657               System.arraycopy(rh, 0, cp, 0, rh.length);\r
658               hiddenColumns.setElementAt(cp, i);\r
659             }\r
660           }\r
661         }\r
662       }\r
663     }\r
664 \r
665   /**\r
666    * ColumnSelection\r
667    */\r
668   public ColumnSelection()\r
669   {\r
670   }\r
671 }\r