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    * ColumnSelection\r
666    */\r
667   public ColumnSelection()\r
668   {\r
669   }\r
670 \r
671   public String[] getVisibleSequenceStrings(int start, int end, SequenceI[] seqs) {\r
672     int i,iSize=seqs.length;\r
673     String selection[] = new String[iSize];\r
674     if (hiddenColumns!=null && hiddenColumns.size()>0)\r
675     {\r
676       for (i=0; i<iSize; i++) {\r
677         StringBuffer visibleSeq = new StringBuffer();\r
678         Vector regions = getHiddenColumns();\r
679 \r
680         int blockStart = start, blockEnd=end;\r
681         int [] region;\r
682         int hideStart, hideEnd;\r
683 \r
684         for (int j = 0; j < regions.size(); j++)\r
685         {\r
686           region = (int[]) regions.elementAt(j);\r
687           hideStart = region[0];\r
688           hideEnd = region[1];\r
689 \r
690           if(hideStart < start)\r
691           {\r
692             continue;\r
693           }\r
694 \r
695           blockStart = Math.min(blockStart, hideEnd+1);\r
696           blockEnd = Math.min(blockEnd, hideStart);\r
697 \r
698           if(blockStart>blockEnd)\r
699           {\r
700             break;\r
701           }\r
702 \r
703 \r
704           visibleSeq.append(seqs[i].getSequence(blockStart, blockEnd));\r
705 \r
706           blockStart = hideEnd+1;\r
707           blockEnd = end;\r
708         }\r
709 \r
710         if(end>blockStart)\r
711           visibleSeq.append(seqs[i].getSequence(blockStart, end));\r
712 \r
713         selection[i] = visibleSeq.toString();\r
714       }\r
715     }\r
716     else\r
717     {\r
718       for(i=0; i<iSize; i++)\r
719       {\r
720         selection[i] = seqs[i].getSequence(start, end);\r
721       }\r
722     }\r
723 \r
724     return selection;\r
725   }\r
726   /**\r
727    * return all visible segments between the given start and end boundaries\r
728    *\r
729    * @param start (first column inclusive from 0)\r
730    * @param end (last column - not inclusive)\r
731    * @return int[] {i_start, i_end, ..} where intervals lie in start<=i_start<=i_end<end\r
732    */\r
733   public int[] getVisibleContigs(int start, int end) {\r
734     if (hiddenColumns!=null && hiddenColumns.size()>0)\r
735     {\r
736       Vector visiblecontigs=new Vector();\r
737       Vector regions = getHiddenColumns();\r
738 \r
739       int vstart = start;\r
740       int [] region;\r
741       int hideStart, hideEnd;\r
742 \r
743       for (int j = 0; vstart<end && j < regions.size(); j++)\r
744       {\r
745         region = (int[]) regions.elementAt(j);\r
746         hideStart = region[0];\r
747         hideEnd = region[1];\r
748 \r
749         if(hideEnd < vstart)\r
750         {\r
751           continue;\r
752         }\r
753         if (hideStart>vstart) {\r
754           visiblecontigs.addElement(new int[] {vstart, hideStart-1});\r
755         }\r
756         vstart=hideEnd+1;\r
757       }\r
758 \r
759       if(vstart<end)\r
760         visiblecontigs.addElement(new int[] { vstart, end-1});\r
761       int[] vcontigs = new int[visiblecontigs.size()*2];\r
762       for (int i=0,j=visiblecontigs.size(); i<j; i++) {\r
763         int [] vc = (int[]) visiblecontigs.elementAt(i);\r
764         visiblecontigs.setElementAt(null, i);\r
765         vcontigs[i*2] = vc[0];\r
766         vcontigs[i*2+1] = vc[1];\r
767       }\r
768       visiblecontigs.removeAllElements();\r
769       return vcontigs;\r
770     }\r
771     else\r
772     {\r
773       return new int[] { start, end-1 };\r
774     }\r
775   }\r
776 }\r