Merge branch 'releases/Release_2_11_4_Branch'
[jalview.git] / datamodel / AlignmentView.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
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
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.datamodel;
22
23 import jalview.util.MessageManager;
24 import jalview.util.ShiftList;
25
26 import java.io.PrintStream;
27 import java.util.Enumeration;
28 import java.util.List;
29 import java.util.Vector;
30
31 /**
32  * Transient object compactly representing a 'view' of an alignment - with
33  * discontinuities marked. Extended in Jalview 2.7 to optionally record sequence
34  * groups and specific selected regions on the alignment.
35  */
36 public class AlignmentView
37 {
38   private SeqCigar[] sequences = null;
39
40   private int[] contigs = null;
41
42   private int width = 0;
43
44   private int firstCol = 0;
45
46   /**
47    * one or more ScGroup objects, which are referenced by each seqCigar's group
48    * membership
49    */
50   private Vector scGroups;
51
52   private boolean isNa = false;
53
54   /**
55    * false if the view concerns peptides
56    * 
57    * @return
58    */
59   public boolean isNa()
60   {
61     return isNa;
62   }
63
64   /**
65    * Group defined over SeqCigars. Unlike AlignmentI associated groups, each
66    * SequenceGroup hold just the essential properties for the group, but no
67    * references to the sequences involved. SeqCigars hold references to the
68    * seuqenceGroup entities themselves.
69    */
70   private class ScGroup
71   {
72     public Vector seqs;
73
74     public SequenceGroup sg;
75
76     ScGroup()
77     {
78       seqs = new Vector();
79     }
80   }
81
82   /**
83    * vector of selected seqCigars. This vector is also referenced by each
84    * seqCigar contained in it.
85    */
86   private Vector selected;
87
88   /**
89    * Construct an alignmentView from a live jalview alignment view. Note -
90    * hidden rows will be excluded from alignmentView Note: JAL-1179
91    * 
92    * @param alignment
93    *          - alignment as referenced by an AlignViewport
94    * @param columnSelection
95    *          -
96    * @param selection
97    * @param hasHiddenColumns
98    *          - mark the hidden columns in columnSelection as hidden in the view
99    * @param selectedRegionOnly
100    *          - when set, only include the selected region in the view,
101    *          otherwise just mark the selected region on the constructed view.
102    * @param recordGroups
103    *          - when set, any groups on the given alignment will be marked on
104    *          the view
105    */
106   public AlignmentView(AlignmentI alignment,
107           ColumnSelection columnSelection, SequenceGroup selection,
108           boolean hasHiddenColumns, boolean selectedRegionOnly,
109           boolean recordGroups)
110   {
111     // refactored from AlignViewport.getAlignmentView(selectedOnly);
112     this(new jalview.datamodel.CigarArray(alignment,
113             (hasHiddenColumns ? columnSelection : null),
114             (selectedRegionOnly ? selection : null)),
115             (selectedRegionOnly && selection != null) ? selection
116                     .getStartRes() : 0);
117     isNa = alignment.isNucleotide();
118     // walk down SeqCigar array and Alignment Array - optionally restricted by
119     // selected region.
120     // test group membership for each sequence in each group, store membership
121     // and record non-empty groups in group list.
122     // record / sub-select selected region on the alignment view
123     SequenceI[] selseqs;
124     if (selection != null && selection.getSize() > 0)
125     {
126       List<SequenceI> sel = selection.getSequences(null);
127       this.selected = new Vector();
128       selseqs = selection
129               .getSequencesInOrder(alignment, selectedRegionOnly);
130     }
131     else
132     {
133       selseqs = alignment.getSequencesArray();
134     }
135
136     // get the alignment's group list and make a copy
137     Vector grps = new Vector();
138     List<SequenceGroup> gg = alignment.getGroups();
139     grps.addAll(gg);
140     ScGroup[] sgrps = null;
141     boolean addedgps[] = null;
142     if (grps != null)
143     {
144       SequenceGroup sg;
145       if (selection != null && selectedRegionOnly)
146       {
147         // trim annotation to the region being stored.
148         // strip out any groups that do not actually intersect with the
149         // visible and selected region
150         int ssel = selection.getStartRes(), esel = selection.getEndRes();
151         Vector isg = new Vector();
152         Enumeration en = grps.elements();
153         while (en.hasMoreElements())
154         {
155           sg = (SequenceGroup) en.nextElement();
156
157           if (!(sg.getStartRes() > esel || sg.getEndRes() < ssel))
158           {
159             // adjust bounds of new group, if necessary.
160             if (sg.getStartRes() < ssel)
161             {
162               sg.setStartRes(ssel);
163             }
164             if (sg.getEndRes() > esel)
165             {
166               sg.setEndRes(esel);
167             }
168             sg.setStartRes(sg.getStartRes() - ssel + 1);
169             sg.setEndRes(sg.getEndRes() - ssel + 1);
170
171             isg.addElement(sg);
172           }
173         }
174         grps = isg;
175       }
176
177       sgrps = new ScGroup[grps.size()];
178       addedgps = new boolean[grps.size()];
179       for (int g = 0; g < sgrps.length; g++)
180       {
181         sg = (SequenceGroup) grps.elementAt(g);
182         sgrps[g] = new ScGroup();
183         sgrps[g].sg = new SequenceGroup(sg);
184         addedgps[g] = false;
185         grps.setElementAt(sg.getSequences(null), g);
186       }
187       // grps now contains vectors (should be sets) for each group, so we can
188       // track when we've done with the group
189     }
190     int csi = 0;
191     for (int i = 0; i < selseqs.length; i++)
192     {
193       if (selseqs[i] != null)
194       {
195         if (selection != null && selection.getSize() > 0
196                 && !selectedRegionOnly)
197         {
198           sequences[csi].setGroupMembership(selected);
199           selected.addElement(sequences[csi]);
200         }
201         if (grps != null)
202         {
203           for (int sg = 0; sg < sgrps.length; sg++)
204           {
205             if (((Vector) grps.elementAt(sg)).contains(selseqs[i]))
206             {
207               sequences[csi].setGroupMembership(sgrps[sg]);
208               sgrps[sg].sg.deleteSequence(selseqs[i], false);
209               sgrps[sg].seqs.addElement(sequences[csi]);
210               if (!addedgps[sg])
211               {
212                 if (scGroups == null)
213                 {
214                   scGroups = new Vector();
215                 }
216                 addedgps[sg] = true;
217                 scGroups.addElement(sgrps[sg]);
218               }
219             }
220           }
221         }
222         csi++;
223       }
224     }
225     // finally, delete the remaining sequences (if any) not selected
226     for (int sg = 0; sg < sgrps.length; sg++)
227     {
228       SequenceI[] sqs = sgrps[sg].sg.getSequencesAsArray(null);
229       for (int si = 0; si < sqs.length; si++)
230       {
231         sgrps[sg].sg.deleteSequence(sqs[si], false);
232       }
233       sgrps[sg] = null;
234     }
235   }
236
237   /**
238    * construct an alignmentView from a SeqCigarArray. Errors are thrown if the
239    * seqcigararray.isSeqCigarArray() flag is not set.
240    */
241   public AlignmentView(CigarArray seqcigararray)
242   {
243     if (!seqcigararray.isSeqCigarArray())
244     {
245       throw new Error(MessageManager.getString("error.implementation_error_can_only_make_alignmnet_from_cigararray"));
246     }
247     // contigs = seqcigararray.applyDeletions();
248     contigs = seqcigararray.getDeletedRegions();
249     sequences = seqcigararray.getSeqCigarArray();
250     width = seqcigararray.getWidth(); // visible width
251   }
252
253   /**
254    * Create an alignmentView where the first column corresponds with the
255    * 'firstcol' column of some reference alignment
256    * 
257    * @param sdata
258    * @param firstcol
259    */
260   public AlignmentView(CigarArray sdata, int firstcol)
261   {
262     this(sdata);
263     firstCol = firstcol;
264   }
265
266   public void setSequences(SeqCigar[] sequences)
267   {
268     this.sequences = sequences;
269   }
270
271   public void setContigs(int[] contigs)
272   {
273     this.contigs = contigs;
274   }
275
276   public SeqCigar[] getSequences()
277   {
278     return sequences;
279   }
280
281   /**
282    * @see CigarArray.getDeletedRegions
283    * @return int[] { vis_start, sym_start, length }
284    */
285   public int[] getContigs()
286   {
287     return contigs;
288   }
289
290   /**
291    * get the full alignment and a columnselection object marking the hidden
292    * regions
293    * 
294    * @param gapCharacter
295    *          char
296    * @return Object[] { SequenceI[], ColumnSelection}
297    */
298   public Object[] getAlignmentAndColumnSelection(char gapCharacter)
299   {
300     ColumnSelection colsel = new ColumnSelection();
301
302     return new Object[]
303     {
304         SeqCigar.createAlignmentSequences(sequences, gapCharacter, colsel,
305                 contigs), colsel };
306   }
307
308   /**
309    * return the visible alignment corresponding to this view. Sequences in this
310    * alignment are edited versions of the parent sequences - where hidden
311    * regions have been removed. NOTE: the sequence data in this alignment is not
312    * complete!
313    * 
314    * @param c
315    * @return
316    */
317   public AlignmentI getVisibleAlignment(char c)
318   {
319     SequenceI[] aln = getVisibleSeqs(c);
320
321     AlignmentI vcal = new Alignment(aln);
322     addPrunedGroupsInOrder(vcal, -1, -1, true);
323     return vcal;
324   }
325
326   /**
327    * add groups from view to the given alignment
328    * 
329    * @param vcal
330    * @param gstart
331    *          -1 or 0 to width-1
332    * @param gend
333    *          -1 or gstart to width-1
334    * @param viscontigs
335    *          - true if vcal is alignment of the visible regions of the view
336    *          (e.g. as returned from getVisibleAlignment)
337    */
338   private void addPrunedGroupsInOrder(AlignmentI vcal, int gstart,
339           int gend, boolean viscontigs)
340   {
341     boolean r = false;
342     if (gstart > -1 && gstart <= gend)
343     {
344       r = true;
345     }
346
347     SequenceI[] aln = vcal.getSequencesArray();
348     {
349       /**
350        * prune any groups to the visible coordinates of the alignment.
351        */
352       {
353         int nvg = (scGroups != null) ? scGroups.size() : 0;
354         if (nvg > 0)
355         {
356           SequenceGroup[] nsg = new SequenceGroup[nvg];
357           for (int g = 0; g < nvg; g++)
358           {
359             SequenceGroup sg = ((ScGroup) scGroups.elementAt(g)).sg;
360             if (r)
361             {
362               if (sg.getStartRes() > gend || sg.getEndRes() < gstart)
363               {
364                 // Skip this group
365                 nsg[g] = null;
366                 continue;
367               }
368             }
369
370             // clone group properties
371             nsg[g] = new SequenceGroup(sg);
372
373             // may need to shift/trim start and end ?
374             if (r && !viscontigs)
375             {
376               // Not fully tested code - routine not yet called with
377               // viscontigs==false
378               if (nsg[g].getStartRes() < gstart)
379               {
380                 nsg[g].setStartRes(0);
381               }
382               else
383               {
384                 nsg[g].setStartRes(nsg[g].getStartRes() - gstart);
385                 nsg[g].setEndRes(nsg[g].getEndRes() - gstart);
386               }
387               if (nsg[g].getEndRes() > (gend - gstart))
388               {
389                 nsg[g].setEndRes(gend - gstart);
390               }
391             }
392           }
393           if (viscontigs)
394           {
395             // prune groups to cover just the visible positions between
396             // gstart/gend.
397             if (contigs != null)
398             {
399               int p = 0;
400               ShiftList prune = new ShiftList();
401               if (r)
402               {
403                 // adjust for start of alignment within visible window.
404                 prune.addShift(gstart, -gstart); //
405               }
406               for (int h = 0; h < contigs.length; h += 3)
407               {
408                 {
409                   prune.addShift(p + contigs[h + 1], contigs[h + 2]
410                           - contigs[h + 1]);
411                 }
412                 p = contigs[h + 1] + contigs[h + 2];
413               }
414               for (int g = 0; g < nsg.length; g++)
415               {
416                 if (nsg[g] != null)
417                 {
418                   int s = nsg[g].getStartRes(), t = nsg[g].getEndRes();
419                   int w = 1 + t - s;
420                   if (r)
421                   {
422                     if (s < gstart)
423                     {
424                       s = gstart;
425                     }
426                     if (t > gend)
427                     {
428                       t = gend;
429                     }
430                   }
431                   s = prune.shift(s);
432                   t = prune.shift(t);
433                   nsg[g].setStartRes(s);
434                   nsg[g].setEndRes(t);
435                 }
436               }
437             }
438           }
439
440           for (int nsq = 0; nsq < aln.length; nsq++)
441           {
442             for (int g = 0; g < nvg; g++)
443             {
444               if (nsg[g] != null
445                       && sequences[nsq].isMemberOf(scGroups.elementAt(g)))
446               {
447                 nsg[g].addSequence(aln[nsq], false);
448               }
449             }
450           }
451           for (int g = 0; g < nvg; g++)
452           {
453             if (nsg[g] != null && nsg[g].getSize() > 0)
454             {
455               vcal.addGroup(nsg[g]);
456             }
457             nsg[g] = null;
458           }
459         }
460       }
461     }
462   }
463
464   /**
465    * generate sequence array corresponding to the visible parts of the
466    * alignment.
467    * 
468    * @param c
469    * @return
470    */
471   private SequenceI[] getVisibleSeqs(char c)
472   {
473     SequenceI[] aln = new SequenceI[sequences.length];
474     for (int i = 0, j = sequences.length; i < j; i++)
475     {
476       aln[i] = sequences[i].getSeq('-');
477     }
478     // Remove hidden regions from sequence objects.
479     String seqs[] = getSequenceStrings('-');
480     for (int i = 0, j = aln.length; i < j; i++)
481     {
482       aln[i].setSequence(seqs[i]);
483     }
484     return aln;
485   }
486
487   /**
488    * creates new alignment objects for all contiguous visible segments
489    * 
490    * @param c
491    * @param start
492    * @param end
493    * @param regionOfInterest
494    *          specify which sequences to include (or null to include all
495    *          sequences)
496    * @return AlignmentI[] - all alignments where each sequence is a subsequence
497    *         constructed from visible contig regions of view
498    */
499   public AlignmentI[] getVisibleContigAlignments(char c)
500   {
501     int nvc = 0;
502     int[] vcontigs = getVisibleContigs();
503     SequenceI[][] contigviews = getVisibleContigs(c);
504     AlignmentI[] vcals = new AlignmentI[contigviews.length];
505     for (nvc = 0; nvc < contigviews.length; nvc++)
506     {
507       vcals[nvc] = new Alignment(contigviews[nvc]);
508       if (scGroups != null && scGroups.size() > 0)
509       {
510         addPrunedGroupsInOrder(vcals[nvc], vcontigs[nvc * 2],
511                 vcontigs[nvc * 2 + 1], true);
512       }
513     }
514     return vcals;
515   }
516
517   /**
518    * get an array of visible sequence strings for a view on an alignment using
519    * the given gap character
520    * 
521    * @param c
522    *          char
523    * @return String[]
524    */
525   public String[] getSequenceStrings(char c)
526   {
527     String[] seqs = new String[sequences.length];
528     for (int n = 0; n < sequences.length; n++)
529     {
530       String fullseq = sequences[n].getSequenceString(c);
531       if (contigs != null)
532       {
533         seqs[n] = "";
534         int p = 0;
535         for (int h = 0; h < contigs.length; h += 3)
536         {
537           seqs[n] += fullseq.substring(p, contigs[h + 1]);
538           p = contigs[h + 1] + contigs[h + 2];
539         }
540         seqs[n] += fullseq.substring(p);
541       }
542       else
543       {
544         seqs[n] = fullseq;
545       }
546     }
547     return seqs;
548   }
549
550   /**
551    * 
552    * @return visible number of columns in alignment view
553    */
554   public int getWidth()
555   {
556     return width;
557   }
558
559   protected void setWidth(int width)
560   {
561     this.width = width;
562   }
563
564   /**
565    * get the contiguous subalignments in an alignment view.
566    * 
567    * @param gapCharacter
568    *          char
569    * @return SequenceI[][]
570    */
571   public SequenceI[][] getVisibleContigs(char gapCharacter)
572   {
573     SequenceI[][] smsa;
574     int njobs = 1;
575     if (sequences == null || width <= 0)
576     {
577       return null;
578     }
579     if (contigs != null && contigs.length > 0)
580     {
581       int start = 0;
582       njobs = 0;
583       int fwidth = width;
584       for (int contig = 0; contig < contigs.length; contig += 3)
585       {
586         if ((contigs[contig + 1] - start) > 0)
587         {
588           njobs++;
589         }
590         fwidth += contigs[contig + 2]; // end up with full region width
591         // (including hidden regions)
592         start = contigs[contig + 1] + contigs[contig + 2];
593       }
594       if (start < fwidth)
595       {
596         njobs++;
597       }
598       smsa = new SequenceI[njobs][];
599       start = 0;
600       int j = 0;
601       for (int contig = 0; contig < contigs.length; contig += 3)
602       {
603         if (contigs[contig + 1] - start > 0)
604         {
605           SequenceI mseq[] = new SequenceI[sequences.length];
606           for (int s = 0; s < mseq.length; s++)
607           {
608             mseq[s] = sequences[s].getSeq(gapCharacter).getSubSequence(
609                     start, contigs[contig + 1]);
610           }
611           smsa[j] = mseq;
612           j++;
613         }
614         start = contigs[contig + 1] + contigs[contig + 2];
615       }
616       if (start < fwidth)
617       {
618         SequenceI mseq[] = new SequenceI[sequences.length];
619         for (int s = 0; s < mseq.length; s++)
620         {
621           mseq[s] = sequences[s].getSeq(gapCharacter).getSubSequence(start,
622                   fwidth + 1);
623         }
624         smsa[j] = mseq;
625         j++;
626       }
627     }
628     else
629     {
630       smsa = new SequenceI[1][];
631       smsa[0] = new SequenceI[sequences.length];
632       for (int s = 0; s < sequences.length; s++)
633       {
634         smsa[0][s] = sequences[s].getSeq(gapCharacter);
635       }
636     }
637     return smsa;
638   }
639
640   /**
641    * return full msa and hidden regions with visible blocks replaced with new
642    * sub alignments
643    * 
644    * @param nvismsa
645    *          SequenceI[][]
646    * @param orders
647    *          AlignmentOrder[] corresponding to each SequenceI[] block.
648    * @return Object[]
649    */
650   public Object[] getUpdatedView(SequenceI[][] nvismsa,
651           AlignmentOrder[] orders, char gapCharacter)
652   {
653     if (sequences == null || width <= 0)
654     {
655       throw new Error(MessageManager.getString("error.empty_view_cannot_be_updated"));
656     }
657     if (nvismsa == null)
658     {
659       throw new Error(
660               "nvismsa==null. use getAlignmentAndColumnSelection() instead.");
661     }
662     if (contigs != null && contigs.length > 0)
663     {
664       SequenceI[] alignment = new SequenceI[sequences.length];
665       ColumnSelection columnselection = new ColumnSelection();
666       if (contigs != null && contigs.length > 0)
667       {
668         int start = 0;
669         int nwidth = 0;
670         int owidth = width;
671         int j = 0;
672         for (int contig = 0; contig < contigs.length; contig += 3)
673         {
674           owidth += contigs[contig + 2]; // recover final column width
675           if (contigs[contig + 1] - start > 0)
676           {
677             int swidth = 0; // subalignment width
678             if (nvismsa[j] != null)
679             {
680               SequenceI mseq[] = nvismsa[j];
681               AlignmentOrder order = (orders == null) ? null : orders[j];
682               j++;
683               if (mseq.length != sequences.length)
684               {
685                 throw new Error(MessageManager.formatMessage("error.mismatch_between_number_of_sequences_in_block", new String[]{Integer.valueOf(j).toString(),Integer.valueOf(mseq.length).toString(),Integer.valueOf(sequences.length).toString() }));
686               }
687               swidth = mseq[0].getLength(); // JBPNote: could ensure padded
688               // here.
689               for (int s = 0; s < mseq.length; s++)
690               {
691                 if (alignment[s] == null)
692                 {
693                   alignment[s] = mseq[s];
694                 }
695                 else
696                 {
697                   alignment[s].setSequence(alignment[s]
698                           .getSequenceAsString()
699                           + mseq[s].getSequenceAsString());
700                   if (mseq[s].getStart() <= mseq[s].getEnd())
701                   {
702                     alignment[s].setEnd(mseq[s].getEnd());
703                   }
704                   if (order != null)
705                   {
706                     order.updateSequence(mseq[s], alignment[s]);
707                   }
708                 }
709               }
710             }
711             else
712             {
713               // recover original alignment block or place gaps
714               if (true)
715               {
716                 // recover input data
717                 for (int s = 0; s < sequences.length; s++)
718                 {
719                   SequenceI oseq = sequences[s].getSeq(gapCharacter)
720                           .getSubSequence(start, contigs[contig + 1]);
721                   if (swidth < oseq.getLength())
722                   {
723                     swidth = oseq.getLength();
724                   }
725                   if (alignment[s] == null)
726                   {
727                     alignment[s] = oseq;
728                   }
729                   else
730                   {
731                     alignment[s].setSequence(alignment[s]
732                             .getSequenceAsString()
733                             + oseq.getSequenceAsString());
734                     if (oseq.getEnd() >= oseq.getStart())
735                     {
736                       alignment[s].setEnd(oseq.getEnd());
737                     }
738                   }
739                 }
740
741               }
742               j++;
743             }
744             nwidth += swidth;
745           }
746           // advance to begining of visible region
747           start = contigs[contig + 1] + contigs[contig + 2];
748           // add hidden segment to right of next region
749           for (int s = 0; s < sequences.length; s++)
750           {
751             SequenceI hseq = sequences[s].getSeq(gapCharacter)
752                     .getSubSequence(contigs[contig + 1], start);
753             if (alignment[s] == null)
754             {
755               alignment[s] = hseq;
756             }
757             else
758             {
759               alignment[s].setSequence(alignment[s].getSequenceAsString()
760                       + hseq.getSequenceAsString());
761               if (hseq.getEnd() >= hseq.getStart())
762               {
763                 alignment[s].setEnd(hseq.getEnd());
764               }
765             }
766           }
767           // mark hidden segment as hidden in the new alignment
768           columnselection.hideColumns(nwidth, nwidth + contigs[contig + 2]
769                   - 1);
770           nwidth += contigs[contig + 2];
771         }
772         // Do final segment - if it exists
773         if (j < nvismsa.length)
774         {
775           int swidth = 0;
776           if (nvismsa[j] != null)
777           {
778             SequenceI mseq[] = nvismsa[j];
779             AlignmentOrder order = (orders != null) ? orders[j] : null;
780             swidth = mseq[0].getLength();
781             for (int s = 0; s < mseq.length; s++)
782             {
783               if (alignment[s] == null)
784               {
785                 alignment[s] = mseq[s];
786               }
787               else
788               {
789                 alignment[s].setSequence(alignment[s].getSequenceAsString()
790                         + mseq[s].getSequenceAsString());
791                 if (mseq[s].getEnd() >= mseq[s].getStart())
792                 {
793                   alignment[s].setEnd(mseq[s].getEnd());
794                 }
795                 if (order != null)
796                 {
797                   order.updateSequence(mseq[s], alignment[s]);
798                 }
799               }
800             }
801           }
802           else
803           {
804             if (start < owidth)
805             {
806               // recover input data or place gaps
807               if (true)
808               {
809                 // recover input data
810                 for (int s = 0; s < sequences.length; s++)
811                 {
812                   SequenceI oseq = sequences[s].getSeq(gapCharacter)
813                           .getSubSequence(start, owidth + 1);
814                   if (swidth < oseq.getLength())
815                   {
816                     swidth = oseq.getLength();
817                   }
818                   if (alignment[s] == null)
819                   {
820                     alignment[s] = oseq;
821                   }
822                   else
823                   {
824                     alignment[s].setSequence(alignment[s]
825                             .getSequenceAsString()
826                             + oseq.getSequenceAsString());
827                     if (oseq.getEnd() >= oseq.getStart())
828                     {
829                       alignment[s].setEnd(oseq.getEnd());
830                     }
831                   }
832                 }
833                 nwidth += swidth;
834               }
835               else
836               {
837                 // place gaps.
838                 throw new Error(MessageManager.getString("error.padding_not_yet_implemented"));
839               }
840             }
841           }
842         }
843       }
844       return new Object[]
845       { alignment, columnselection };
846     }
847     else
848     {
849       if (nvismsa.length != 1)
850       {
851         throw new Error(MessageManager.formatMessage("error.mismatch_between_visible_blocks_to_update_and_number_of_contigs_in_view", new String[]{Integer.valueOf(nvismsa.length).toString()}));
852       }
853       if (nvismsa[0] != null)
854       {
855         return new Object[]
856         { nvismsa[0], new ColumnSelection() };
857       }
858       else
859       {
860         return getAlignmentAndColumnSelection(gapCharacter);
861       }
862     }
863   }
864
865   /**
866    * returns simple array of start end positions of visible range on alignment.
867    * vis_start and vis_end are inclusive - use
868    * SequenceI.getSubSequence(vis_start, vis_end+1) to recover visible sequence
869    * from underlying alignment.
870    * 
871    * @return int[] { start_i, end_i } for 1<i<n visible regions.
872    */
873   public int[] getVisibleContigs()
874   {
875     if (contigs != null && contigs.length > 0)
876     {
877       int start = 0;
878       int nvis = 0;
879       int fwidth = width;
880       for (int contig = 0; contig < contigs.length; contig += 3)
881       {
882         if ((contigs[contig + 1] - start) > 0)
883         {
884           nvis++;
885         }
886         fwidth += contigs[contig + 2]; // end up with full region width
887         // (including hidden regions)
888         start = contigs[contig + 1] + contigs[contig + 2];
889       }
890       if (start < fwidth)
891       {
892         nvis++;
893       }
894       int viscontigs[] = new int[nvis * 2];
895       nvis = 0;
896       start = 0;
897       for (int contig = 0; contig < contigs.length; contig += 3)
898       {
899         if ((contigs[contig + 1] - start) > 0)
900         {
901           viscontigs[nvis] = start;
902           viscontigs[nvis + 1] = contigs[contig + 1] - 1; // end is inclusive
903           nvis += 2;
904         }
905         start = contigs[contig + 1] + contigs[contig + 2];
906       }
907       if (start < fwidth)
908       {
909         viscontigs[nvis] = start;
910         viscontigs[nvis + 1] = fwidth; // end is inclusive
911         nvis += 2;
912       }
913       return viscontigs;
914     }
915     else
916     {
917       return new int[]
918       { 0, width };
919     }
920   }
921
922   /**
923    * 
924    * @return position of first visible column of AlignmentView within its
925    *         parent's alignment reference frame
926    */
927   public int getAlignmentOrigin()
928   {
929     return firstCol;
930   }
931
932   /**
933    * compute a deletion map for the current view according to the given
934    * gap/match map
935    * 
936    * @param gapMap
937    *          (as returned from SequenceI.gapMap())
938    * @return int[] {intersection of visible regions with gapMap)
939    */
940   public int[] getVisibleContigMapFor(int[] gapMap)
941   {
942     int[] delMap = null;
943     int[] viscontigs = getVisibleContigs();
944     int spos = 0;
945     int i = 0;
946     if (viscontigs != null)
947     {
948       // viscontigs maps from a subset of the gapMap to the gapMap, so it will
949       // always be equal to or shorter than gapMap
950       delMap = new int[gapMap.length];
951       for (int contig = 0; contig < viscontigs.length; contig += 2)
952       {
953
954         while (spos < gapMap.length && gapMap[spos] < viscontigs[contig])
955         {
956           spos++;
957         }
958         while (spos < gapMap.length
959                 && gapMap[spos] <= viscontigs[contig + 1])
960         {
961           delMap[i++] = spos++;
962         }
963       }
964       int tmap[] = new int[i];
965       System.arraycopy(delMap, 0, tmap, 0, i);
966       delMap = tmap;
967     }
968     return delMap;
969   }
970
971   /**
972    * apply the getSeq(gc) method to each sequence cigar, and return the array of
973    * edited sequences, optionally with hidden regions removed.
974    * 
975    * @param gc
976    *          gap character to use for insertions
977    * @param delete
978    *          remove hidden regions from sequences. Note: currently implemented
979    *          in a memory inefficient way - space needed is 2*result set for
980    *          deletion
981    * 
982    * @return SequenceI[]
983    */
984   public SequenceI[] getEditedSequences(char gc, boolean delete)
985   {
986     SeqCigar[] msf = getSequences();
987     SequenceI[] aln = new SequenceI[msf.length];
988     for (int i = 0, j = msf.length; i < j; i++)
989     {
990       aln[i] = msf[i].getSeq(gc);
991     }
992     if (delete)
993     {
994       String[] sqs = getSequenceStrings(gc);
995       for (int i = 0; i < sqs.length; i++)
996       {
997         aln[i].setSequence(sqs[i]);
998         sqs[i] = null;
999       }
1000     }
1001     return aln;
1002   }
1003
1004   public static void summariseAlignmentView(AlignmentView view,
1005           PrintStream os)
1006   {
1007     os.print("View has " + view.sequences.length + " of which ");
1008     if (view.selected == null)
1009     {
1010       os.print("None");
1011     }
1012     else
1013     {
1014       os.print(" " + view.selected.size());
1015     }
1016     os.println(" are selected.");
1017     os.print("View is " + view.getWidth() + " columns wide");
1018     int viswid = 0;
1019     int[] contigs = view.getContigs();
1020     if (contigs != null)
1021     {
1022       viswid = view.width;
1023       for (int i = 0; i < contigs.length; i += 3)
1024       {
1025         viswid += contigs[i + 2];
1026       }
1027       os.println("with " + viswid + " visible columns spread over "
1028               + contigs.length / 3 + " regions.");
1029     }
1030     else
1031     {
1032       viswid = view.width;
1033       os.println(".");
1034     }
1035     if (view.scGroups != null)
1036     {
1037       os.println("There are " + view.scGroups.size()
1038               + " groups defined on the view.");
1039       for (int g = 0; g < view.scGroups.size(); g++)
1040       {
1041         ScGroup sgr = (ScGroup) view.scGroups.elementAt(g);
1042         os.println("Group " + g + ": Name = " + sgr.sg.getName()
1043                 + " Contains " + sgr.seqs.size() + " Seqs.");
1044         os.println("This group runs from " + sgr.sg.getStartRes() + " to "
1045                 + sgr.sg.getEndRes());
1046         for (int s = 0; s < sgr.seqs.size(); s++)
1047         {
1048           if (!((SeqCigar) sgr.seqs.elementAt(s)).isMemberOf(sgr))
1049           {
1050             os.println("** WARNING: sequence "
1051                     + ((SeqCigar) sgr.seqs.elementAt(s)).toString()
1052                     + " is not marked as member of group.");
1053           }
1054         }
1055       }
1056       AlignmentI visal = view.getVisibleAlignment('-');
1057       if (visal != null)
1058       {
1059         os.println("Vis. alignment is " + visal.getWidth()
1060                 + " wide and has " + visal.getHeight() + " seqs.");
1061         if (visal.getGroups() != null && visal.getGroups().size() > 0)
1062         {
1063
1064           int i = 1;
1065           for (SequenceGroup sg : visal.getGroups())
1066           {
1067             os.println("Group " + (i++) + " begins at column "
1068                     + sg.getStartRes() + " and ends at " + sg.getEndRes());
1069           }
1070         }
1071       }
1072     }
1073   }
1074
1075   public static void testSelectionViews(AlignmentI alignment,
1076           ColumnSelection csel, SequenceGroup selection)
1077   {
1078     System.out.println("Testing standard view creation:\n");
1079     AlignmentView view = null;
1080     try
1081     {
1082       System.out
1083               .println("View with no hidden columns, no limit to selection, no groups to be collected:");
1084       view = new AlignmentView(alignment, csel, selection, false, false,
1085               false);
1086       summariseAlignmentView(view, System.out);
1087
1088     } catch (Exception e)
1089     {
1090       e.printStackTrace();
1091       System.err
1092               .println("Failed to generate alignment with selection but no groups marked.");
1093     }
1094     try
1095     {
1096       System.out
1097               .println("View with no hidden columns, no limit to selection, and all groups to be collected:");
1098       view = new AlignmentView(alignment, csel, selection, false, false,
1099               true);
1100       summariseAlignmentView(view, System.out);
1101     } catch (Exception e)
1102     {
1103       e.printStackTrace();
1104       System.err
1105               .println("Failed to generate alignment with selection marked but no groups marked.");
1106     }
1107     try
1108     {
1109       System.out
1110               .println("View with no hidden columns, limited to selection and no groups to be collected:");
1111       view = new AlignmentView(alignment, csel, selection, false, true,
1112               false);
1113       summariseAlignmentView(view, System.out);
1114     } catch (Exception e)
1115     {
1116       e.printStackTrace();
1117       System.err
1118               .println("Failed to generate alignment with selection restricted but no groups marked.");
1119     }
1120     try
1121     {
1122       System.out
1123               .println("View with no hidden columns, limited to selection, and all groups to be collected:");
1124       view = new AlignmentView(alignment, csel, selection, false, true,
1125               true);
1126       summariseAlignmentView(view, System.out);
1127     } catch (Exception e)
1128     {
1129       e.printStackTrace();
1130       System.err
1131               .println("Failed to generate alignment with selection restricted and groups marked.");
1132     }
1133     try
1134     {
1135       System.out
1136               .println("View *with* hidden columns, no limit to selection, no groups to be collected:");
1137       view = new AlignmentView(alignment, csel, selection, true, false,
1138               false);
1139       summariseAlignmentView(view, System.out);
1140     } catch (Exception e)
1141     {
1142       e.printStackTrace();
1143       System.err
1144               .println("Failed to generate alignment with selection but no groups marked.");
1145     }
1146     try
1147     {
1148       System.out
1149               .println("View *with* hidden columns, no limit to selection, and all groups to be collected:");
1150       view = new AlignmentView(alignment, csel, selection, true, false,
1151               true);
1152       summariseAlignmentView(view, System.out);
1153     } catch (Exception e)
1154     {
1155       e.printStackTrace();
1156       System.err
1157               .println("Failed to generate alignment with selection marked but no groups marked.");
1158     }
1159     try
1160     {
1161       System.out
1162               .println("View *with* hidden columns, limited to selection and no groups to be collected:");
1163       view = new AlignmentView(alignment, csel, selection, true, true,
1164               false);
1165       summariseAlignmentView(view, System.out);
1166     } catch (Exception e)
1167     {
1168       e.printStackTrace();
1169       System.err
1170               .println("Failed to generate alignment with selection restricted but no groups marked.");
1171     }
1172     try
1173     {
1174       System.out
1175               .println("View *with* hidden columns, limited to selection, and all groups to be collected:");
1176       view = new AlignmentView(alignment, csel, selection, true, true, true);
1177       summariseAlignmentView(view, System.out);
1178     } catch (Exception e)
1179     {
1180       e.printStackTrace();
1181       System.err
1182               .println("Failed to generate alignment with selection restricted and groups marked.");
1183     }
1184
1185   }
1186 }