JAL-972 workaround for working out source URL for pre DAS 1.6 sources.
[jalview.git] / src / jalview / datamodel / ColumnSelection.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7)\r
3  * Copyright (C) 2011 J Procter, AM Waterhouse, J Engelhardt, LM Lui, 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\r
512    * alignment when hidden columns exist. If the column is not visible, then the\r
513    * left-most visible column will always be returned.\r
514    * \r
515    * @param hiddenColumn\r
516    *          int\r
517    * @return int\r
518    */\r
519   public int findColumnPosition(int hiddenColumn)\r
520   {\r
521     int result = hiddenColumn;\r
522     if (hiddenColumns != null)\r
523     {\r
524       int index = 0;\r
525       int[] region;\r
526       do\r
527       {\r
528         region = (int[]) hiddenColumns.elementAt(index++);\r
529         if (hiddenColumn > region[1])\r
530         {\r
531           result -= region[1] + 1 - region[0];\r
532         }\r
533       } while ((hiddenColumn > region[1]) && (index < hiddenColumns.size()));\r
534       if (hiddenColumn > region[0] && hiddenColumn < region[1])\r
535       {\r
536         return region[0] + hiddenColumn - result;\r
537       }\r
538     }\r
539     return result; // return the shifted position after removing hidden columns.\r
540   }\r
541 \r
542   /**\r
543    * Use this method to determine where the next hiddenRegion starts\r
544    */\r
545   public int findHiddenRegionPosition(int hiddenRegion)\r
546   {\r
547     int result = 0;\r
548     if (hiddenColumns != null)\r
549     {\r
550       int index = 0;\r
551       int gaps = 0;\r
552       do\r
553       {\r
554         int[] region = (int[]) hiddenColumns.elementAt(index);\r
555         if (hiddenRegion == 0)\r
556         {\r
557           return region[0];\r
558         }\r
559 \r
560         gaps += region[1] + 1 - region[0];\r
561         result = region[1] + 1;\r
562         index++;\r
563       } while (index < hiddenRegion + 1);\r
564 \r
565       result -= gaps;\r
566     }\r
567 \r
568     return result;\r
569   }\r
570 \r
571   /**\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
574    * \r
575    * @param index\r
576    *          int\r
577    */\r
578   public int getHiddenBoundaryRight(int alPos)\r
579   {\r
580     if (hiddenColumns != null)\r
581     {\r
582       int index = 0;\r
583       do\r
584       {\r
585         int[] region = (int[]) hiddenColumns.elementAt(index);\r
586         if (alPos < region[0])\r
587         {\r
588           return region[0];\r
589         }\r
590 \r
591         index++;\r
592       } while (index < hiddenColumns.size());\r
593     }\r
594 \r
595     return alPos;\r
596 \r
597   }\r
598 \r
599   /**\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
602    * \r
603    * @param index\r
604    *          int\r
605    */\r
606   public int getHiddenBoundaryLeft(int alPos)\r
607   {\r
608     if (hiddenColumns != null)\r
609     {\r
610       int index = hiddenColumns.size() - 1;\r
611       do\r
612       {\r
613         int[] region = (int[]) hiddenColumns.elementAt(index);\r
614         if (alPos > region[1])\r
615         {\r
616           return region[1];\r
617         }\r
618 \r
619         index--;\r
620       } while (index > -1);\r
621     }\r
622 \r
623     return alPos;\r
624 \r
625   }\r
626 \r
627   public void hideSelectedColumns()\r
628   {\r
629     while (size() > 0)\r
630     {\r
631       int column = ((Integer) getSelected().firstElement()).intValue();\r
632       hideColumns(column);\r
633     }\r
634 \r
635   }\r
636 \r
637   public void hideColumns(int start, int end)\r
638   {\r
639     if (hiddenColumns == null)\r
640     {\r
641       hiddenColumns = new Vector();\r
642     }\r
643 \r
644     boolean added = false;\r
645     boolean overlap = false;\r
646 \r
647     for (int i = 0; i < hiddenColumns.size(); i++)\r
648     {\r
649       int[] region = (int[]) hiddenColumns.elementAt(i);\r
650       if (start <= region[1] && end >= region[0])\r
651       {\r
652         hiddenColumns.removeElementAt(i);\r
653         overlap = true;\r
654         break;\r
655       }\r
656       else if (end < region[0] && start < region[0])\r
657       {\r
658         hiddenColumns.insertElementAt(new int[]\r
659         { start, end }, i);\r
660         added = true;\r
661         break;\r
662       }\r
663     }\r
664 \r
665     if (overlap)\r
666     {\r
667       hideColumns(start, end);\r
668     }\r
669     else if (!added)\r
670     {\r
671       hiddenColumns.addElement(new int[]\r
672       { start, end });\r
673     }\r
674 \r
675   }\r
676 \r
677   /**\r
678    * This method will find a range of selected columns around the column\r
679    * specified\r
680    * \r
681    * @param res\r
682    *          int\r
683    */\r
684   public void hideColumns(int col)\r
685   {\r
686     // First find out range of columns to hide\r
687     int min = col, max = col + 1;\r
688     while (contains(min))\r
689     {\r
690       removeElement(min);\r
691       min--;\r
692     }\r
693 \r
694     while (contains(max))\r
695     {\r
696       removeElement(max);\r
697       max++;\r
698     }\r
699 \r
700     min++;\r
701     max--;\r
702     if (min > max)\r
703     {\r
704       min = max;\r
705     }\r
706 \r
707     hideColumns(min, max);\r
708   }\r
709 \r
710   public void revealAllHiddenColumns()\r
711   {\r
712     if (hiddenColumns != null)\r
713     {\r
714       for (int i = 0; i < hiddenColumns.size(); i++)\r
715       {\r
716         int[] region = (int[]) hiddenColumns.elementAt(i);\r
717         for (int j = region[0]; j < region[1] + 1; j++)\r
718         {\r
719           addElement(j);\r
720         }\r
721       }\r
722     }\r
723 \r
724     hiddenColumns = null;\r
725   }\r
726 \r
727   public void revealHiddenColumns(int res)\r
728   {\r
729     for (int i = 0; i < hiddenColumns.size(); i++)\r
730     {\r
731       int[] region = (int[]) hiddenColumns.elementAt(i);\r
732       if (res == region[0])\r
733       {\r
734         for (int j = region[0]; j < region[1] + 1; j++)\r
735         {\r
736           addElement(j);\r
737         }\r
738 \r
739         hiddenColumns.removeElement(region);\r
740         break;\r
741       }\r
742     }\r
743     if (hiddenColumns.size() == 0)\r
744     {\r
745       hiddenColumns = null;\r
746     }\r
747   }\r
748 \r
749   public boolean isVisible(int column)\r
750   {\r
751     if (hiddenColumns != null)\r
752       for (int i = 0; i < hiddenColumns.size(); i++)\r
753       {\r
754         int[] region = (int[]) hiddenColumns.elementAt(i);\r
755         if (column >= region[0] && column <= region[1])\r
756         {\r
757           return false;\r
758         }\r
759       }\r
760 \r
761     return true;\r
762   }\r
763 \r
764   /**\r
765    * Copy constructor\r
766    * \r
767    * @param copy\r
768    */\r
769   public ColumnSelection(ColumnSelection copy)\r
770   {\r
771     if (copy != null)\r
772     {\r
773       if (copy.selected != null)\r
774       {\r
775         selected = new Vector();\r
776         for (int i = 0, j = copy.selected.size(); i < j; i++)\r
777         {\r
778           selected.addElement(copy.selected.elementAt(i));\r
779         }\r
780       }\r
781       if (copy.hiddenColumns != null)\r
782       {\r
783         hiddenColumns = new Vector(copy.hiddenColumns.size());\r
784         for (int i = 0, j = copy.hiddenColumns.size(); i < j; i++)\r
785         {\r
786           int[] rh, cp;\r
787           rh = (int[]) copy.hiddenColumns.elementAt(i);\r
788           if (rh != null)\r
789           {\r
790             cp = new int[rh.length];\r
791             System.arraycopy(rh, 0, cp, 0, rh.length);\r
792             hiddenColumns.addElement(cp);\r
793           }\r
794         }\r
795       }\r
796     }\r
797   }\r
798 \r
799   /**\r
800    * ColumnSelection\r
801    */\r
802   public ColumnSelection()\r
803   {\r
804   }\r
805 \r
806   public String[] getVisibleSequenceStrings(int start, int end,\r
807           SequenceI[] seqs)\r
808   {\r
809     int i, iSize = seqs.length;\r
810     String selection[] = new String[iSize];\r
811     if (hiddenColumns != null && hiddenColumns.size() > 0)\r
812     {\r
813       for (i = 0; i < iSize; i++)\r
814       {\r
815         StringBuffer visibleSeq = new StringBuffer();\r
816         Vector regions = getHiddenColumns();\r
817 \r
818         int blockStart = start, blockEnd = end;\r
819         int[] region;\r
820         int hideStart, hideEnd;\r
821 \r
822         for (int j = 0; j < regions.size(); j++)\r
823         {\r
824           region = (int[]) regions.elementAt(j);\r
825           hideStart = region[0];\r
826           hideEnd = region[1];\r
827 \r
828           if (hideStart < start)\r
829           {\r
830             continue;\r
831           }\r
832 \r
833           blockStart = Math.min(blockStart, hideEnd + 1);\r
834           blockEnd = Math.min(blockEnd, hideStart);\r
835 \r
836           if (blockStart > blockEnd)\r
837           {\r
838             break;\r
839           }\r
840 \r
841           visibleSeq.append(seqs[i].getSequence(blockStart, blockEnd));\r
842 \r
843           blockStart = hideEnd + 1;\r
844           blockEnd = end;\r
845         }\r
846 \r
847         if (end > blockStart)\r
848         {\r
849           visibleSeq.append(seqs[i].getSequence(blockStart, end));\r
850         }\r
851 \r
852         selection[i] = visibleSeq.toString();\r
853       }\r
854     }\r
855     else\r
856     {\r
857       for (i = 0; i < iSize; i++)\r
858       {\r
859         selection[i] = seqs[i].getSequenceAsString(start, end);\r
860       }\r
861     }\r
862 \r
863     return selection;\r
864   }\r
865 \r
866   /**\r
867    * return all visible segments between the given start and end boundaries\r
868    * \r
869    * @param start\r
870    *          (first column inclusive from 0)\r
871    * @param end\r
872    *          (last column - not inclusive)\r
873    * @return int[] {i_start, i_end, ..} where intervals lie in\r
874    *         start<=i_start<=i_end<end\r
875    */\r
876   public int[] getVisibleContigs(int start, int end)\r
877   {\r
878     if (hiddenColumns != null && hiddenColumns.size() > 0)\r
879     {\r
880       Vector visiblecontigs = new Vector();\r
881       Vector regions = getHiddenColumns();\r
882 \r
883       int vstart = start;\r
884       int[] region;\r
885       int hideStart, hideEnd;\r
886 \r
887       for (int j = 0; vstart < end && j < regions.size(); j++)\r
888       {\r
889         region = (int[]) regions.elementAt(j);\r
890         hideStart = region[0];\r
891         hideEnd = region[1];\r
892 \r
893         if (hideEnd < vstart)\r
894         {\r
895           continue;\r
896         }\r
897         if (hideStart > vstart)\r
898         {\r
899           visiblecontigs.addElement(new int[]\r
900           { vstart, hideStart - 1 });\r
901         }\r
902         vstart = hideEnd + 1;\r
903       }\r
904 \r
905       if (vstart < end)\r
906       {\r
907         visiblecontigs.addElement(new int[]\r
908         { vstart, end - 1 });\r
909       }\r
910       int[] vcontigs = new int[visiblecontigs.size() * 2];\r
911       for (int i = 0, j = visiblecontigs.size(); i < j; i++)\r
912       {\r
913         int[] vc = (int[]) visiblecontigs.elementAt(i);\r
914         visiblecontigs.setElementAt(null, i);\r
915         vcontigs[i * 2] = vc[0];\r
916         vcontigs[i * 2 + 1] = vc[1];\r
917       }\r
918       visiblecontigs.removeAllElements();\r
919       return vcontigs;\r
920     }\r
921     else\r
922     {\r
923       return new int[]\r
924       { start, end - 1 };\r
925     }\r
926   }\r
927 \r
928   /**\r
929    * delete any columns in alignmentAnnotation that are hidden (including\r
930    * sequence associated annotation).\r
931    * \r
932    * @param alignmentAnnotation\r
933    */\r
934   public void makeVisibleAnnotation(AlignmentAnnotation alignmentAnnotation)\r
935   {\r
936     makeVisibleAnnotation(-1, -1, alignmentAnnotation);\r
937   }\r
938 \r
939   /**\r
940    * delete any columns in alignmentAnnotation that are hidden (including\r
941    * sequence associated annotation).\r
942    * \r
943    * @param start\r
944    *          remove any annotation to the right of this column\r
945    * @param end\r
946    *          remove any annotation to the left of this column\r
947    * @param alignmentAnnotation\r
948    *          the annotation to operate on\r
949    */\r
950   public void makeVisibleAnnotation(int start, int end,\r
951           AlignmentAnnotation alignmentAnnotation)\r
952   {\r
953     if (alignmentAnnotation.annotations == null)\r
954     {\r
955       return;\r
956     }\r
957     if (start == end && end == -1)\r
958     {\r
959       start = 0;\r
960       end = alignmentAnnotation.annotations.length;\r
961     }\r
962     if (hiddenColumns != null && hiddenColumns.size() > 0)\r
963     {\r
964       // then mangle the alignmentAnnotation annotation array\r
965       Vector annels = new Vector();\r
966       Annotation[] els = null;\r
967       Vector regions = getHiddenColumns();\r
968       int blockStart = start, blockEnd = end;\r
969       int[] region;\r
970       int hideStart, hideEnd, w = 0;\r
971 \r
972       for (int j = 0; j < regions.size(); j++)\r
973       {\r
974         region = (int[]) regions.elementAt(j);\r
975         hideStart = region[0];\r
976         hideEnd = region[1];\r
977 \r
978         if (hideStart < start)\r
979         {\r
980           continue;\r
981         }\r
982 \r
983         blockStart = Math.min(blockStart, hideEnd + 1);\r
984         blockEnd = Math.min(blockEnd, hideStart);\r
985 \r
986         if (blockStart > blockEnd)\r
987         {\r
988           break;\r
989         }\r
990 \r
991         annels.addElement(els = new Annotation[blockEnd - blockStart]);\r
992         System.arraycopy(alignmentAnnotation.annotations, blockStart, els,\r
993                 0, els.length);\r
994         w += els.length;\r
995         blockStart = hideEnd + 1;\r
996         blockEnd = end;\r
997       }\r
998 \r
999       if (end > blockStart)\r
1000       {\r
1001         annels.addElement(els = new Annotation[end - blockStart + 1]);\r
1002         if ((els.length + blockStart) <= alignmentAnnotation.annotations.length)\r
1003         {\r
1004           // copy just the visible segment of the annotation row\r
1005           System.arraycopy(alignmentAnnotation.annotations, blockStart,\r
1006                   els, 0, els.length);\r
1007         }\r
1008         else\r
1009         {\r
1010           // copy to the end of the annotation row\r
1011           System.arraycopy(alignmentAnnotation.annotations, blockStart,\r
1012                   els, 0,\r
1013                   (alignmentAnnotation.annotations.length - blockStart));\r
1014         }\r
1015         w += els.length;\r
1016       }\r
1017       if (w == 0)\r
1018         return;\r
1019       Enumeration e = annels.elements();\r
1020       alignmentAnnotation.annotations = new Annotation[w];\r
1021       w = 0;\r
1022       while (e.hasMoreElements())\r
1023       {\r
1024         Annotation[] chnk = (Annotation[]) e.nextElement();\r
1025         System.arraycopy(chnk, 0, alignmentAnnotation.annotations, w,\r
1026                 chnk.length);\r
1027         w += chnk.length;\r
1028       }\r
1029     }\r
1030     else\r
1031     {\r
1032       alignmentAnnotation.restrict(start, end);\r
1033     }\r
1034   }\r
1035 \r
1036   /**\r
1037    * Invert the column selection from first to end-1. leaves hiddenColumns\r
1038    * untouched (and unselected)\r
1039    * \r
1040    * @param first\r
1041    * @param end\r
1042    */\r
1043   public void invertColumnSelection(int first, int width)\r
1044   {\r
1045     boolean hasHidden = hiddenColumns != null && hiddenColumns.size() > 0;\r
1046     for (int i = first; i < width; i++)\r
1047     {\r
1048       if (contains(i))\r
1049       {\r
1050         removeElement(i);\r
1051       }\r
1052       else\r
1053       {\r
1054         if (!hasHidden || isVisible(i))\r
1055         {\r
1056           addElement(i);\r
1057         }\r
1058       }\r
1059     }\r
1060   }\r
1061 \r
1062   /**\r
1063    * add in any unselected columns from the given column selection, excluding\r
1064    * any that are hidden.\r
1065    * \r
1066    * @param colsel\r
1067    */\r
1068   public void addElementsFrom(ColumnSelection colsel)\r
1069   {\r
1070     if (colsel != null && colsel.size() > 0)\r
1071     {\r
1072       Enumeration e = colsel.getSelected().elements();\r
1073       while (e.hasMoreElements())\r
1074       {\r
1075         Object eo = e.nextElement();\r
1076         if (hiddenColumns != null && isVisible(((Integer) eo).intValue()))\r
1077         {\r
1078           if (!selected.contains(eo))\r
1079           {\r
1080             selected.addElement(eo);\r
1081           }\r
1082         }\r
1083       }\r
1084     }\r
1085   }\r
1086 \r
1087   /**\r
1088    * set the selected columns the given column selection, excluding any columns\r
1089    * that are hidden.\r
1090    * \r
1091    * @param colsel\r
1092    */\r
1093   public void setElementsFrom(ColumnSelection colsel)\r
1094   {\r
1095     selected = new Vector();\r
1096     if (colsel.selected != null && colsel.selected.size() > 0)\r
1097     {\r
1098       if (hiddenColumns != null && hiddenColumns.size() > 0)\r
1099       {\r
1100         // only select visible columns in this columns selection\r
1101         selected = new Vector();\r
1102         addElementsFrom(colsel);\r
1103       }\r
1104       else\r
1105       {\r
1106         // add everything regardless\r
1107         Enumeration en = colsel.selected.elements();\r
1108         while (en.hasMoreElements())\r
1109         {\r
1110           selected.addElement(en.nextElement());\r
1111         }\r
1112       }\r
1113     }\r
1114   }\r
1115 \r
1116   /**\r
1117    * Add gaps into the sequences aligned to profileseq under the given\r
1118    * AlignmentView\r
1119    * \r
1120    * @param profileseq\r
1121    * @param al - alignment to have gaps inserted into it\r
1122    * @param input - alignment view where sequence corresponding to profileseq is first entry\r
1123    * @return new Column selection for new alignment view, with insertions into profileseq marked as hidden.\r
1124    */\r
1125   public static ColumnSelection propagateInsertions(SequenceI profileseq,\r
1126           Alignment al, AlignmentView input)\r
1127   {\r
1128     int profsqpos=0;\r
1129     \r
1130 //    return propagateInsertions(profileseq, al, )\r
1131     char gc = al.getGapCharacter();\r
1132     Object[] alandcolsel = input.getAlignmentAndColumnSelection(gc);\r
1133     ColumnSelection nview = (ColumnSelection) alandcolsel[1];\r
1134     SequenceI origseq = ((SequenceI[]) alandcolsel[0])[profsqpos];\r
1135     nview.propagateInsertions(profileseq,\r
1136             al, origseq);\r
1137     return nview;\r
1138   }\r
1139   /**\r
1140    * \r
1141    * @param profileseq - sequence in al which corresponds to origseq \r
1142    * @param al - alignment which is to have gaps inserted into it\r
1143    * @param origseq - sequence corresponding to profileseq which defines gap map for modifying al\r
1144    */\r
1145   public void propagateInsertions(SequenceI profileseq, AlignmentI al, SequenceI origseq)\r
1146   {\r
1147     char gc = al.getGapCharacter();\r
1148     // recover mapping between sequence's non-gap positions and positions \r
1149     // mapping to view.\r
1150     pruneDeletions(ShiftList\r
1151             .parseMap(origseq\r
1152                     .gapMap())); \r
1153     int[] viscontigs = getVisibleContigs(0, profileseq.getLength());\r
1154     int spos = 0;\r
1155     int offset = 0;\r
1156     // input.pruneDeletions(ShiftList.parseMap(((SequenceI[])\r
1157     // alandcolsel[0])[0].gapMap()))\r
1158     // add profile to visible contigs\r
1159     for (int v = 0; v < viscontigs.length; v += 2)\r
1160     {\r
1161       if (viscontigs[v] > spos)\r
1162       {\r
1163         StringBuffer sb = new StringBuffer();\r
1164         for (int s = 0, ns = viscontigs[v] - spos; s < ns; s++)\r
1165         {\r
1166           sb.append(gc);\r
1167         }\r
1168         for (int s = 0, ns = al.getHeight(); s < ns; s++)\r
1169         {\r
1170           SequenceI sqobj = al.getSequenceAt(s);\r
1171           if (sqobj != profileseq)\r
1172           {\r
1173             String sq = al.getSequenceAt(s).getSequenceAsString();\r
1174             if (sq.length() <= spos + offset)\r
1175             {\r
1176               // pad sequence\r
1177               int diff = spos + offset - sq.length() - 1;\r
1178               if (diff > 0)\r
1179               {\r
1180                 // pad gaps\r
1181                 sq = sq + sb;\r
1182                 while ((diff = spos + offset - sq.length() - 1) > 0)\r
1183                 {\r
1184                   //sq = sq\r
1185                   //        + ((diff >= sb.length()) ? sb.toString() : sb\r
1186                   //                .substring(0, diff));\r
1187                   if (diff>=sb.length()) {\r
1188                     sq+=sb.toString();\r
1189                   } else {\r
1190                     char[] buf = new char[diff];\r
1191                     sb.getChars(0, diff, buf, 0);\r
1192                     sq+=buf.toString();\r
1193                   }\r
1194                 }\r
1195               }\r
1196               sq += sb.toString();\r
1197             }\r
1198             else\r
1199             {\r
1200               al.getSequenceAt(s).setSequence(\r
1201                       sq.substring(0, spos + offset) + sb.toString()\r
1202                               + sq.substring(spos + offset));\r
1203             }\r
1204           }\r
1205         }\r
1206         // offset+=sb.length();\r
1207       }\r
1208       spos = viscontigs[v + 1] + 1;\r
1209     }\r
1210     if ((offset + spos) < profileseq.getLength())\r
1211     {\r
1212       // pad the final region with gaps.\r
1213       StringBuffer sb = new StringBuffer();\r
1214       for (int s = 0, ns = profileseq.getLength() - spos - offset; s < ns; s++)\r
1215       {\r
1216         sb.append(gc);\r
1217       }\r
1218       for (int s = 0, ns = al.getHeight(); s < ns; s++)\r
1219       {\r
1220         SequenceI sqobj = al.getSequenceAt(s);\r
1221         if (sqobj==profileseq)\r
1222         {\r
1223           continue;\r
1224         }\r
1225         String sq = sqobj.getSequenceAsString();\r
1226         // pad sequence\r
1227         int diff = origseq.getLength() - sq.length();\r
1228         while (diff > 0)\r
1229         {\r
1230           //sq = sq\r
1231           //        + ((diff >= sb.length()) ? sb.toString() : sb\r
1232           //                .substring(0, diff));\r
1233           if (diff>=sb.length()) {\r
1234             sq+=sb.toString();\r
1235           } else {\r
1236             char[] buf = new char[diff];\r
1237             sb.getChars(0, diff, buf, 0);\r
1238             sq+=buf.toString();\r
1239           }\r
1240           diff = origseq.getLength() - sq.length();\r
1241         }\r
1242       }\r
1243     }\r
1244   }\r
1245 }\r