Jalview 2.6 source licence
[jalview.git] / src / jalview / datamodel / ColumnSelection.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.6)\r
3  * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle\r
4  * \r
5  * This file is part of Jalview.\r
6  * \r
7  * Jalview is free software: you can redistribute it and/or\r
8  * modify it under the terms of the GNU General Public License \r
9  * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\r
10  * \r
11  * Jalview is distributed in the hope that it will be useful, but \r
12  * WITHOUT ANY WARRANTY; without even the implied warranty \r
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR \r
14  * PURPOSE.  See the GNU General Public License for more details.\r
15  * \r
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.\r
17  */\r
18 package jalview.datamodel;\r
19 \r
20 import java.util.*;\r
21 \r
22 import jalview.util.*;\r
23 \r
24 /**\r
25  * NOTE: Columns are zero based.\r
26  */\r
27 public class ColumnSelection\r
28 {\r
29   Vector selected = new Vector();\r
30 \r
31   // Vector of int [] {startCol, endCol}\r
32   Vector hiddenColumns;\r
33 \r
34   /**\r
35    * Add a column to the selection\r
36    * \r
37    * @param col\r
38    *          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\r
61    *          index of column to be removed\r
62    */\r
63   public void removeElement(int col)\r
64   {\r
65     Integer colInt = new Integer(col);\r
66 \r
67     if (selected.contains(colInt))\r
68     {\r
69       selected.removeElement(colInt);\r
70     }\r
71   }\r
72 \r
73   /**\r
74    * removes a range of columns from the selection\r
75    * \r
76    * @param start\r
77    *          int - first column in range to be removed\r
78    * @param end\r
79    *          int - last col\r
80    */\r
81   public void removeElements(int start, int end)\r
82   {\r
83     Integer colInt;\r
84     for (int i = start; i < end; i++)\r
85     {\r
86       colInt = new Integer(i);\r
87       if (selected.contains(colInt))\r
88       {\r
89         selected.removeElement(colInt);\r
90       }\r
91     }\r
92   }\r
93 \r
94   /**\r
95    * \r
96    * @return Vector containing selected columns as Integers\r
97    */\r
98   public Vector getSelected()\r
99   {\r
100     return selected;\r
101   }\r
102 \r
103   /**\r
104    * \r
105    * @param col\r
106    *          index to search for in column selection\r
107    * \r
108    * @return true if Integer(col) is in selection.\r
109    */\r
110   public boolean contains(int col)\r
111   {\r
112     return selected.contains(new Integer(col));\r
113   }\r
114 \r
115   /**\r
116    * Column number at position i in selection\r
117    * \r
118    * @param i\r
119    *          index into selected columns\r
120    * \r
121    * @return column number in alignment\r
122    */\r
123   public int columnAt(int i)\r
124   {\r
125     return ((Integer) selected.elementAt(i)).intValue();\r
126   }\r
127 \r
128   /**\r
129    * DOCUMENT ME!\r
130    * \r
131    * @return DOCUMENT ME!\r
132    */\r
133   public int size()\r
134   {\r
135     return selected.size();\r
136   }\r
137 \r
138   /**\r
139    * rightmost selected column\r
140    * \r
141    * @return rightmost column in alignment that is selected\r
142    */\r
143   public int getMax()\r
144   {\r
145     int max = -1;\r
146 \r
147     for (int i = 0; i < selected.size(); i++)\r
148     {\r
149       if (columnAt(i) > max)\r
150       {\r
151         max = columnAt(i);\r
152       }\r
153     }\r
154 \r
155     return max;\r
156   }\r
157 \r
158   /**\r
159    * Leftmost column in selection\r
160    * \r
161    * @return column index of leftmost column in selection\r
162    */\r
163   public int getMin()\r
164   {\r
165     int min = 1000000000;\r
166 \r
167     for (int i = 0; i < selected.size(); i++)\r
168     {\r
169       if (columnAt(i) < min)\r
170       {\r
171         min = columnAt(i);\r
172       }\r
173     }\r
174 \r
175     return min;\r
176   }\r
177 \r
178   /**\r
179    * propagate shift in alignment columns to column selection\r
180    * \r
181    * @param start\r
182    *          beginning of edit\r
183    * @param left\r
184    *          shift in edit (+ve for removal, or -ve for inserts)\r
185    */\r
186   public Vector compensateForEdit(int start, int change)\r
187   {\r
188     Vector deletedHiddenColumns = null;\r
189     for (int i = 0; i < size(); i++)\r
190     {\r
191       int temp = columnAt(i);\r
192 \r
193       if (temp >= start)\r
194       {\r
195         selected.setElementAt(new Integer(temp - change), i);\r
196       }\r
197     }\r
198 \r
199     if (hiddenColumns != null)\r
200     {\r
201       deletedHiddenColumns = new Vector();\r
202       int hSize = hiddenColumns.size();\r
203       for (int i = 0; i < hSize; i++)\r
204       {\r
205         int[] region = (int[]) hiddenColumns.elementAt(i);\r
206         if (region[0] > start && start + change > region[1])\r
207         {\r
208           deletedHiddenColumns.addElement(hiddenColumns.elementAt(i));\r
209 \r
210           hiddenColumns.removeElementAt(i);\r
211           i--;\r
212           hSize--;\r
213           continue;\r
214         }\r
215 \r
216         if (region[0] > start)\r
217         {\r
218           region[0] -= change;\r
219           region[1] -= change;\r
220         }\r
221 \r
222         if (region[0] < 0)\r
223         {\r
224           region[0] = 0;\r
225         }\r
226 \r
227       }\r
228 \r
229       this.revealHiddenColumns(0);\r
230     }\r
231 \r
232     return deletedHiddenColumns;\r
233   }\r
234 \r
235   /**\r
236    * propagate shift in alignment columns to column selection special version of\r
237    * compensateForEdit - allowing for edits within hidden regions\r
238    * \r
239    * @param start\r
240    *          beginning of edit\r
241    * @param left\r
242    *          shift in edit (+ve for removal, or -ve for inserts)\r
243    */\r
244   private void compensateForDelEdits(int start, int change)\r
245   {\r
246     for (int i = 0; i < size(); i++)\r
247     {\r
248       int temp = columnAt(i);\r
249 \r
250       if (temp >= start)\r
251       {\r
252         selected.setElementAt(new Integer(temp - change), i);\r
253       }\r
254     }\r
255 \r
256     if (hiddenColumns != null)\r
257     {\r
258       for (int i = 0; i < hiddenColumns.size(); i++)\r
259       {\r
260         int[] region = (int[]) hiddenColumns.elementAt(i);\r
261         if (region[0] >= start)\r
262         {\r
263           region[0] -= change;\r
264         }\r
265         if (region[1] >= start)\r
266         {\r
267           region[1] -= change;\r
268         }\r
269         if (region[1] < region[0])\r
270         {\r
271           hiddenColumns.removeElementAt(i--);\r
272         }\r
273 \r
274         if (region[0] < 0)\r
275         {\r
276           region[0] = 0;\r
277         }\r
278         if (region[1] < 0)\r
279         {\r
280           region[1] = 0;\r
281         }\r
282       }\r
283     }\r
284   }\r
285 \r
286   /**\r
287    * Adjust hidden column boundaries based on a series of column additions or\r
288    * deletions in visible regions.\r
289    * \r
290    * @param shiftrecord\r
291    * @return\r
292    */\r
293   public ShiftList compensateForEdits(ShiftList shiftrecord)\r
294   {\r
295     if (shiftrecord != null)\r
296     {\r
297       Vector shifts = shiftrecord.shifts;\r
298       if (shifts != null && shifts.size() > 0)\r
299       {\r
300         int shifted = 0;\r
301         for (int i = 0, j = shifts.size(); i < j; i++)\r
302         {\r
303           int[] sh = (int[]) shifts.elementAt(i);\r
304           // compensateForEdit(shifted+sh[0], sh[1]);\r
305           compensateForDelEdits(shifted + sh[0], sh[1]);\r
306           shifted -= sh[1];\r
307         }\r
308       }\r
309       return shiftrecord.getInverse();\r
310     }\r
311     return null;\r
312   }\r
313 \r
314   /**\r
315    * removes intersection of position,length ranges in deletions from the\r
316    * start,end regions marked in intervals.\r
317    * \r
318    * @param deletions\r
319    * @param intervals\r
320    * @return\r
321    */\r
322   private boolean pruneIntervalVector(Vector deletions, Vector intervals)\r
323   {\r
324     boolean pruned = false;\r
325     int i = 0, j = intervals.size() - 1, s = 0, t = deletions.size() - 1;\r
326     int hr[] = (int[]) intervals.elementAt(i);\r
327     int sr[] = (int[]) deletions.elementAt(s);\r
328     while (i <= j && s <= t)\r
329     {\r
330       boolean trailinghn = hr[1] >= sr[0];\r
331       if (!trailinghn)\r
332       {\r
333         if (i < j)\r
334         {\r
335           hr = (int[]) intervals.elementAt(++i);\r
336         }\r
337         else\r
338         {\r
339           i++;\r
340         }\r
341         continue;\r
342       }\r
343       int endshift = sr[0] + sr[1]; // deletion ranges - -ve means an insert\r
344       if (endshift < hr[0] || endshift < sr[0])\r
345       { // leadinghc disjoint or not a deletion\r
346         if (s < t)\r
347         {\r
348           sr = (int[]) deletions.elementAt(++s);\r
349         }\r
350         else\r
351         {\r
352           s++;\r
353         }\r
354         continue;\r
355       }\r
356       boolean leadinghn = hr[0] >= sr[0];\r
357       boolean leadinghc = hr[0] < endshift;\r
358       boolean trailinghc = hr[1] < endshift;\r
359       if (leadinghn)\r
360       {\r
361         if (trailinghc)\r
362         { // deleted hidden region.\r
363           intervals.removeElementAt(i);\r
364           pruned = true;\r
365           j--;\r
366           if (i <= j)\r
367           {\r
368             hr = (int[]) intervals.elementAt(i);\r
369           }\r
370           continue;\r
371         }\r
372         if (leadinghc)\r
373         {\r
374           hr[0] = endshift; // clip c terminal region\r
375           leadinghn = !leadinghn;\r
376           pruned = true;\r
377         }\r
378       }\r
379       if (!leadinghn)\r
380       {\r
381         if (trailinghc)\r
382         {\r
383           if (trailinghn)\r
384           {\r
385             hr[1] = sr[0] - 1;\r
386             pruned = true;\r
387           }\r
388         }\r
389         else\r
390         {\r
391           // sr contained in hr\r
392           if (s < t)\r
393           {\r
394             sr = (int[]) deletions.elementAt(++s);\r
395           }\r
396           else\r
397           {\r
398             s++;\r
399           }\r
400           continue;\r
401         }\r
402       }\r
403     }\r
404     return pruned; // true if any interval was removed or modified by\r
405     // operations.\r
406   }\r
407 \r
408   private boolean pruneColumnList(Vector deletion, Vector list)\r
409   {\r
410     int s = 0, t = deletion.size();\r
411     int[] sr = (int[]) list.elementAt(s++);\r
412     boolean pruned = false;\r
413     int i = 0, j = list.size();\r
414     while (i < j && s <= t)\r
415     {\r
416       int c = ((Integer) list.elementAt(i++)).intValue();\r
417       if (sr[0] <= c)\r
418       {\r
419         if (sr[1] + sr[0] >= c)\r
420         { // sr[1] -ve means inseriton.\r
421           list.removeElementAt(--i);\r
422           j--;\r
423         }\r
424         else\r
425         {\r
426           if (s < t)\r
427           {\r
428             sr = (int[]) deletion.elementAt(s);\r
429           }\r
430           s++;\r
431         }\r
432       }\r
433     }\r
434     return pruned;\r
435   }\r
436 \r
437   /**\r
438    * remove any hiddenColumns or selected columns and shift remaining based on a\r
439    * series of position, range deletions.\r
440    * \r
441    * @param deletions\r
442    */\r
443   public void pruneDeletions(ShiftList deletions)\r
444   {\r
445     if (deletions != null)\r
446     {\r
447       Vector shifts = deletions.shifts;\r
448       if (shifts != null && shifts.size() > 0)\r
449       {\r
450         // delete any intervals intersecting.\r
451         if (hiddenColumns != null)\r
452         {\r
453           pruneIntervalVector(shifts, hiddenColumns);\r
454           if (hiddenColumns != null && hiddenColumns.size() == 0)\r
455           {\r
456             hiddenColumns = null;\r
457           }\r
458         }\r
459         if (selected != null && selected.size() > 0)\r
460         {\r
461           pruneColumnList(shifts, selected);\r
462           if (selected != null && selected.size() == 0)\r
463           {\r
464             selected = null;\r
465           }\r
466         }\r
467         // and shift the rest.\r
468         this.compensateForEdits(deletions);\r
469       }\r
470     }\r
471   }\r
472 \r
473   /**\r
474    * This Method is used to return all the HiddenColumn regions less than the\r
475    * given index.\r
476    * \r
477    * @param end\r
478    *          int\r
479    * @return Vector\r
480    */\r
481   public Vector getHiddenColumns()\r
482   {\r
483     return hiddenColumns;\r
484   }\r
485 \r
486   /**\r
487    * Return absolute column index for a visible column index\r
488    * \r
489    * @param column\r
490    *          int column index in alignment view\r
491    * @return alignment column index for column\r
492    */\r
493   public int adjustForHiddenColumns(int column)\r
494   {\r
495     int result = column;\r
496     if (hiddenColumns != null)\r
497     {\r
498       for (int i = 0; i < hiddenColumns.size(); i++)\r
499       {\r
500         int[] region = (int[]) hiddenColumns.elementAt(i);\r
501         if (result >= region[0])\r
502         {\r
503           result += region[1] - region[0] + 1;\r
504         }\r
505       }\r
506     }\r
507     return result;\r
508   }\r
509 \r
510   /**\r
511    * Use this method to find out where a column will appear in the visible alignment when\r
512    * hidden columns exist. If the column is not visible, then the left-most visible column will always be returned.\r
513    * \r
514    * @param hiddenColumn\r
515    *          int\r
516    * @return int\r
517    */\r
518   public int findColumnPosition(int hiddenColumn)\r
519   {\r
520     int result = hiddenColumn;\r
521     if (hiddenColumns != null)\r
522     {\r
523       int index = 0;\r
524       int[] region;\r
525       do\r
526       {\r
527         region = (int[]) hiddenColumns.elementAt(index++);\r
528         if (hiddenColumn > region[1])\r
529         {\r
530           result -= region[1] + 1 - region[0];\r
531         }\r
532       } while ((hiddenColumn > region[1]) && (index < hiddenColumns.size()));\r
533       if (hiddenColumn>region[0] && hiddenColumn<region[1])\r
534       {\r
535         return region[0]+hiddenColumn-result;\r
536       }\r
537     }\r
538     return result; // return the shifted position after removing hidden columns.\r
539   }\r
540 \r
541   /**\r
542    * Use this method to determine where the next hiddenRegion starts\r
543    */\r
544   public int findHiddenRegionPosition(int hiddenRegion)\r
545   {\r
546     int result = 0;\r
547     if (hiddenColumns != null)\r
548     {\r
549       int index = 0;\r
550       int gaps = 0;\r
551       do\r
552       {\r
553         int[] region = (int[]) hiddenColumns.elementAt(index);\r
554         if (hiddenRegion == 0)\r
555         {\r
556           return region[0];\r
557         }\r
558 \r
559         gaps += region[1] + 1 - region[0];\r
560         result = region[1] + 1;\r
561         index++;\r
562       } while (index < hiddenRegion + 1);\r
563 \r
564       result -= gaps;\r
565     }\r
566 \r
567     return result;\r
568   }\r
569 \r
570   /**\r
571    * THis method returns the rightmost limit of a region of an alignment with\r
572    * hidden columns. In otherwords, the next hidden column.\r
573    * \r
574    * @param index\r
575    *          int\r
576    */\r
577   public int getHiddenBoundaryRight(int alPos)\r
578   {\r
579     if (hiddenColumns != null)\r
580     {\r
581       int index = 0;\r
582       do\r
583       {\r
584         int[] region = (int[]) hiddenColumns.elementAt(index);\r
585         if (alPos < region[0])\r
586         {\r
587           return region[0];\r
588         }\r
589 \r
590         index++;\r
591       } while (index < hiddenColumns.size());\r
592     }\r
593 \r
594     return alPos;\r
595 \r
596   }\r
597 \r
598   /**\r
599    * This method returns the leftmost limit of a region of an alignment with\r
600    * hidden columns. In otherwords, the previous hidden column.\r
601    * \r
602    * @param index\r
603    *          int\r
604    */\r
605   public int getHiddenBoundaryLeft(int alPos)\r
606   {\r
607     if (hiddenColumns != null)\r
608     {\r
609       int index = hiddenColumns.size() - 1;\r
610       do\r
611       {\r
612         int[] region = (int[]) hiddenColumns.elementAt(index);\r
613         if (alPos > region[1])\r
614         {\r
615           return region[1];\r
616         }\r
617 \r
618         index--;\r
619       } while (index > -1);\r
620     }\r
621 \r
622     return alPos;\r
623 \r
624   }\r
625 \r
626   public void hideSelectedColumns()\r
627   {\r
628     while (size() > 0)\r
629     {\r
630       int column = ((Integer) getSelected().firstElement()).intValue();\r
631       hideColumns(column);\r
632     }\r
633 \r
634   }\r
635 \r
636   public void hideColumns(int start, int end)\r
637   {\r
638     if (hiddenColumns == null)\r
639     {\r
640       hiddenColumns = new Vector();\r
641     }\r
642 \r
643     boolean added = false;\r
644     boolean overlap = false;\r
645 \r
646     for (int i = 0; i < hiddenColumns.size(); i++)\r
647     {\r
648       int[] region = (int[]) hiddenColumns.elementAt(i);\r
649       if (start <= region[1] && end >= region[0])\r
650       {\r
651         hiddenColumns.removeElementAt(i);\r
652         overlap = true;\r
653         break;\r
654       }\r
655       else if (end < region[0] && start < region[0])\r
656       {\r
657         hiddenColumns.insertElementAt(new int[]\r
658         { start, end }, i);\r
659         added = true;\r
660         break;\r
661       }\r
662     }\r
663 \r
664     if (overlap)\r
665     {\r
666       hideColumns(start, end);\r
667     }\r
668     else if (!added)\r
669     {\r
670       hiddenColumns.addElement(new int[]\r
671       { start, end });\r
672     }\r
673 \r
674   }\r
675 \r
676   /**\r
677    * This method will find a range of selected columns around the column\r
678    * specified\r
679    * \r
680    * @param res\r
681    *          int\r
682    */\r
683   public void hideColumns(int col)\r
684   {\r
685     // First find out range of columns to hide\r
686     int min = col, max = col + 1;\r
687     while (contains(min))\r
688     {\r
689       removeElement(min);\r
690       min--;\r
691     }\r
692 \r
693     while (contains(max))\r
694     {\r
695       removeElement(max);\r
696       max++;\r
697     }\r
698 \r
699     min++;\r
700     max--;\r
701     if (min > max)\r
702     {\r
703       min = max;\r
704     }\r
705 \r
706     hideColumns(min, max);\r
707   }\r
708 \r
709   public void revealAllHiddenColumns()\r
710   {\r
711     if (hiddenColumns != null)\r
712     {\r
713       for (int i = 0; i < hiddenColumns.size(); i++)\r
714       {\r
715         int[] region = (int[]) hiddenColumns.elementAt(i);\r
716         for (int j = region[0]; j < region[1] + 1; j++)\r
717         {\r
718           addElement(j);\r
719         }\r
720       }\r
721     }\r
722 \r
723     hiddenColumns = null;\r
724   }\r
725 \r
726   public void revealHiddenColumns(int res)\r
727   {\r
728     for (int i = 0; i < hiddenColumns.size(); i++)\r
729     {\r
730       int[] region = (int[]) hiddenColumns.elementAt(i);\r
731       if (res == region[0])\r
732       {\r
733         for (int j = region[0]; j < region[1] + 1; j++)\r
734         {\r
735           addElement(j);\r
736         }\r
737 \r
738         hiddenColumns.removeElement(region);\r
739         break;\r
740       }\r
741     }\r
742     if (hiddenColumns.size() == 0)\r
743     {\r
744       hiddenColumns = null;\r
745     }\r
746   }\r
747 \r
748   public boolean isVisible(int column)\r
749   {\r
750     if (hiddenColumns != null)\r
751       for (int i = 0; i < hiddenColumns.size(); i++)\r
752       {\r
753         int[] region = (int[]) hiddenColumns.elementAt(i);\r
754         if (column >= region[0] && column <= region[1])\r
755         {\r
756           return false;\r
757         }\r
758       }\r
759 \r
760     return true;\r
761   }\r
762 \r
763   /**\r
764    * Copy constructor\r
765    * \r
766    * @param copy\r
767    */\r
768   public ColumnSelection(ColumnSelection copy)\r
769   {\r
770     if (copy != null)\r
771     {\r
772       if (copy.selected != null)\r
773       {\r
774         selected = new Vector();\r
775         for (int i = 0, j = copy.selected.size(); i < j; i++)\r
776         {\r
777           selected.addElement(copy.selected.elementAt(i));\r
778         }\r
779       }\r
780       if (copy.hiddenColumns != null)\r
781       {\r
782         hiddenColumns = new Vector(copy.hiddenColumns.size());\r
783         for (int i = 0, j = copy.hiddenColumns.size(); i < j; i++)\r
784         {\r
785           int[] rh, cp;\r
786           rh = (int[]) copy.hiddenColumns.elementAt(i);\r
787           if (rh != null)\r
788           {\r
789             cp = new int[rh.length];\r
790             System.arraycopy(rh, 0, cp, 0, rh.length);\r
791             hiddenColumns.addElement(cp);\r
792           }\r
793         }\r
794       }\r
795     }\r
796   }\r
797 \r
798   /**\r
799    * ColumnSelection\r
800    */\r
801   public ColumnSelection()\r
802   {\r
803   }\r
804 \r
805   public String[] getVisibleSequenceStrings(int start, int end,\r
806           SequenceI[] seqs)\r
807   {\r
808     int i, iSize = seqs.length;\r
809     String selection[] = new String[iSize];\r
810     if (hiddenColumns != null && hiddenColumns.size() > 0)\r
811     {\r
812       for (i = 0; i < iSize; i++)\r
813       {\r
814         StringBuffer visibleSeq = new StringBuffer();\r
815         Vector regions = getHiddenColumns();\r
816 \r
817         int blockStart = start, blockEnd = end;\r
818         int[] region;\r
819         int hideStart, hideEnd;\r
820 \r
821         for (int j = 0; j < regions.size(); j++)\r
822         {\r
823           region = (int[]) regions.elementAt(j);\r
824           hideStart = region[0];\r
825           hideEnd = region[1];\r
826 \r
827           if (hideStart < start)\r
828           {\r
829             continue;\r
830           }\r
831 \r
832           blockStart = Math.min(blockStart, hideEnd + 1);\r
833           blockEnd = Math.min(blockEnd, hideStart);\r
834 \r
835           if (blockStart > blockEnd)\r
836           {\r
837             break;\r
838           }\r
839 \r
840           visibleSeq.append(seqs[i].getSequence(blockStart, blockEnd));\r
841 \r
842           blockStart = hideEnd + 1;\r
843           blockEnd = end;\r
844         }\r
845 \r
846         if (end > blockStart)\r
847         {\r
848           visibleSeq.append(seqs[i].getSequence(blockStart, end));\r
849         }\r
850 \r
851         selection[i] = visibleSeq.toString();\r
852       }\r
853     }\r
854     else\r
855     {\r
856       for (i = 0; i < iSize; i++)\r
857       {\r
858         selection[i] = seqs[i].getSequenceAsString(start, end);\r
859       }\r
860     }\r
861 \r
862     return selection;\r
863   }\r
864 \r
865   /**\r
866    * return all visible segments between the given start and end boundaries\r
867    * \r
868    * @param start\r
869    *          (first column inclusive from 0)\r
870    * @param end\r
871    *          (last column - not inclusive)\r
872    * @return int[] {i_start, i_end, ..} where intervals lie in\r
873    *         start<=i_start<=i_end<end\r
874    */\r
875   public int[] getVisibleContigs(int start, int end)\r
876   {\r
877     if (hiddenColumns != null && hiddenColumns.size() > 0)\r
878     {\r
879       Vector visiblecontigs = new Vector();\r
880       Vector regions = getHiddenColumns();\r
881 \r
882       int vstart = start;\r
883       int[] region;\r
884       int hideStart, hideEnd;\r
885 \r
886       for (int j = 0; vstart < end && j < regions.size(); j++)\r
887       {\r
888         region = (int[]) regions.elementAt(j);\r
889         hideStart = region[0];\r
890         hideEnd = region[1];\r
891 \r
892         if (hideEnd < vstart)\r
893         {\r
894           continue;\r
895         }\r
896         if (hideStart > vstart)\r
897         {\r
898           visiblecontigs.addElement(new int[]\r
899           { vstart, hideStart - 1 });\r
900         }\r
901         vstart = hideEnd + 1;\r
902       }\r
903 \r
904       if (vstart < end)\r
905       {\r
906         visiblecontigs.addElement(new int[]\r
907         { vstart, end - 1 });\r
908       }\r
909       int[] vcontigs = new int[visiblecontigs.size() * 2];\r
910       for (int i = 0, j = visiblecontigs.size(); i < j; i++)\r
911       {\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
916       }\r
917       visiblecontigs.removeAllElements();\r
918       return vcontigs;\r
919     }\r
920     else\r
921     {\r
922       return new int[]\r
923       { start, end - 1 };\r
924     }\r
925   }\r
926 \r
927   /**\r
928    * delete any columns in alignmentAnnotation that are hidden (including\r
929    * sequence associated annotation).\r
930    * \r
931    * @param alignmentAnnotation\r
932    */\r
933   public void makeVisibleAnnotation(AlignmentAnnotation alignmentAnnotation)\r
934   {\r
935     makeVisibleAnnotation(-1, -1, alignmentAnnotation);\r
936   }\r
937 \r
938   /**\r
939    * delete any columns in alignmentAnnotation that are hidden (including\r
940    * sequence associated annotation).\r
941    * \r
942    * @param start\r
943    *          remove any annotation to the right of this column\r
944    * @param end\r
945    *          remove any annotation to the left of this column\r
946    * @param alignmentAnnotation\r
947    *          the annotation to operate on\r
948    */\r
949   public void makeVisibleAnnotation(int start, int end,\r
950           AlignmentAnnotation alignmentAnnotation)\r
951   {\r
952     if (alignmentAnnotation.annotations == null)\r
953     {\r
954       return;\r
955     }\r
956     if (start == end && end == -1)\r
957     {\r
958       start = 0;\r
959       end = alignmentAnnotation.annotations.length;\r
960     }\r
961     if (hiddenColumns != null && hiddenColumns.size() > 0)\r
962     {\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
968       int[] region;\r
969       int hideStart, hideEnd, w = 0;\r
970 \r
971       for (int j = 0; j < regions.size(); j++)\r
972       {\r
973         region = (int[]) regions.elementAt(j);\r
974         hideStart = region[0];\r
975         hideEnd = region[1];\r
976 \r
977         if (hideStart < start)\r
978         {\r
979           continue;\r
980         }\r
981 \r
982         blockStart = Math.min(blockStart, hideEnd + 1);\r
983         blockEnd = Math.min(blockEnd, hideStart);\r
984 \r
985         if (blockStart > blockEnd)\r
986         {\r
987           break;\r
988         }\r
989 \r
990         annels.addElement(els = new Annotation[blockEnd - blockStart]);\r
991         System.arraycopy(alignmentAnnotation.annotations, blockStart, els,\r
992                 0, els.length);\r
993         w += els.length;\r
994         blockStart = hideEnd + 1;\r
995         blockEnd = end;\r
996       }\r
997 \r
998       if (end > blockStart)\r
999       {\r
1000         annels.addElement(els = new Annotation[end - blockStart + 1]);\r
1001         if ((els.length + blockStart) <= alignmentAnnotation.annotations.length)\r
1002         {\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
1006         }\r
1007         else\r
1008         {\r
1009           // copy to the end of the annotation row\r
1010           System.arraycopy(alignmentAnnotation.annotations, blockStart,\r
1011                   els, 0,\r
1012                   (alignmentAnnotation.annotations.length - blockStart));\r
1013         }\r
1014         w += els.length;\r
1015       }\r
1016       if (w == 0)\r
1017         return;\r
1018       Enumeration e = annels.elements();\r
1019       alignmentAnnotation.annotations = new Annotation[w];\r
1020       w = 0;\r
1021       while (e.hasMoreElements())\r
1022       {\r
1023         Annotation[] chnk = (Annotation[]) e.nextElement();\r
1024         System.arraycopy(chnk, 0, alignmentAnnotation.annotations, w,\r
1025                 chnk.length);\r
1026         w += chnk.length;\r
1027       }\r
1028     }\r
1029     else\r
1030     {\r
1031       alignmentAnnotation.restrict(start, end);\r
1032     }\r
1033   }\r
1034 \r
1035   /**\r
1036    * Invert the column selection from first to end-1. leaves hiddenColumns\r
1037    * untouched (and unselected)\r
1038    * \r
1039    * @param first\r
1040    * @param end\r
1041    */\r
1042   public void invertColumnSelection(int first, int width)\r
1043   {\r
1044     boolean hasHidden = hiddenColumns != null && hiddenColumns.size() > 0;\r
1045     for (int i = first; i < width; i++)\r
1046     {\r
1047       if (contains(i))\r
1048       {\r
1049         removeElement(i);\r
1050       }\r
1051       else\r
1052       {\r
1053         if (!hasHidden || isVisible(i))\r
1054         {\r
1055           addElement(i);\r
1056         }\r
1057       }\r
1058     }\r
1059   }\r
1060 \r
1061   /**\r
1062    * add in any unselected columns from the given column selection, excluding\r
1063    * any that are hidden.\r
1064    * \r
1065    * @param colsel\r
1066    */\r
1067   public void addElementsFrom(ColumnSelection colsel)\r
1068   {\r
1069     if (colsel != null && colsel.size() > 0)\r
1070     {\r
1071       Enumeration e = colsel.getSelected().elements();\r
1072       while (e.hasMoreElements())\r
1073       {\r
1074         Object eo = e.nextElement();\r
1075         if (hiddenColumns != null && isVisible(((Integer) eo).intValue()))\r
1076         {\r
1077           if (!selected.contains(eo))\r
1078           {\r
1079             selected.addElement(eo);\r
1080           }\r
1081         }\r
1082       }\r
1083     }\r
1084   }\r
1085 \r
1086   /**\r
1087    * set the selected columns the given column selection, excluding any columns\r
1088    * that are hidden.\r
1089    * \r
1090    * @param colsel\r
1091    */\r
1092   public void setElementsFrom(ColumnSelection colsel)\r
1093   {\r
1094     selected = new Vector();\r
1095     if (colsel.selected != null && colsel.selected.size() > 0)\r
1096     {\r
1097       if (hiddenColumns != null && hiddenColumns.size() > 0)\r
1098       {\r
1099         // only select visible columns in this columns selection\r
1100         selected = new Vector();\r
1101         addElementsFrom(colsel);\r
1102       }\r
1103       else\r
1104       {\r
1105         // add everything regardless\r
1106         Enumeration en = colsel.selected.elements();\r
1107         while (en.hasMoreElements())\r
1108         {\r
1109           selected.addElement(en.nextElement());\r
1110         }\r
1111       }\r
1112     }\r
1113   }\r
1114 }\r