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