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