JAL-1517 source formatting
[jalview.git] / src / jalview / 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.ShiftList;
24
25 import java.io.PrintStream;
26 import java.util.Enumeration;
27 import java.util.List;
28 import java.util.Vector;
29
30 /**
31  * Transient object compactly representing a 'view' of an alignment - with
32  * discontinuities marked. Extended in Jalview 2.7 to optionally record sequence
33  * groups and specific selected regions on the alignment.
34  */
35 public class AlignmentView
36 {
37   private SeqCigar[] sequences = null;
38
39   private int[] contigs = null;
40
41   private int width = 0;
42
43   private int firstCol = 0;
44
45   /**
46    * one or more ScGroup objects, which are referenced by each seqCigar's group
47    * membership
48    */
49   private Vector scGroups;
50
51   private boolean isNa = false;
52
53   /**
54    * false if the view concerns peptides
55    * 
56    * @return
57    */
58   public boolean isNa()
59   {
60     return isNa;
61   }
62
63   /**
64    * Group defined over SeqCigars. Unlike AlignmentI associated groups, each
65    * SequenceGroup hold just the essential properties for the group, but no
66    * references to the sequences involved. SeqCigars hold references to the
67    * seuqenceGroup entities themselves.
68    */
69   private class ScGroup
70   {
71     public Vector seqs;
72
73     public SequenceGroup sg;
74
75     ScGroup()
76     {
77       seqs = new Vector();
78     }
79   }
80
81   /**
82    * vector of selected seqCigars. This vector is also referenced by each
83    * seqCigar contained in it.
84    */
85   private Vector selected;
86
87   /**
88    * Construct an alignmentView from a live jalview alignment view. Note -
89    * hidden rows will be excluded from alignmentView Note: JAL-1179
90    * 
91    * @param alignment
92    *          - alignment as referenced by an AlignViewport
93    * @param columnSelection
94    *          -
95    * @param selection
96    * @param hasHiddenColumns
97    *          - mark the hidden columns in columnSelection as hidden in the view
98    * @param selectedRegionOnly
99    *          - when set, only include the selected region in the view,
100    *          otherwise just mark the selected region on the constructed view.
101    * @param recordGroups
102    *          - when set, any groups on the given alignment will be marked on
103    *          the view
104    */
105   public AlignmentView(AlignmentI alignment,
106           ColumnSelection columnSelection, SequenceGroup selection,
107           boolean hasHiddenColumns, boolean selectedRegionOnly,
108           boolean recordGroups)
109   {
110     // refactored from AlignViewport.getAlignmentView(selectedOnly);
111     this(new jalview.datamodel.CigarArray(alignment,
112             (hasHiddenColumns ? columnSelection : null),
113             (selectedRegionOnly ? selection : null)),
114             (selectedRegionOnly && selection != null) ? selection
115                     .getStartRes() : 0);
116     isNa = alignment.isNucleotide();
117     // walk down SeqCigar array and Alignment Array - optionally restricted by
118     // selected region.
119     // test group membership for each sequence in each group, store membership
120     // and record non-empty groups in group list.
121     // record / sub-select selected region on the alignment view
122     SequenceI[] selseqs;
123     if (selection != null && selection.getSize() > 0)
124     {
125       List<SequenceI> sel = selection.getSequences(null);
126       this.selected = new Vector();
127       selseqs = selection
128               .getSequencesInOrder(alignment, selectedRegionOnly);
129     }
130     else
131     {
132       selseqs = alignment.getSequencesArray();
133     }
134
135     // get the alignment's group list and make a copy
136     Vector grps = new Vector();
137     List<SequenceGroup> gg = alignment.getGroups();
138     grps.addAll(gg);
139     ScGroup[] sgrps = null;
140     boolean addedgps[] = null;
141     if (grps != null)
142     {
143       SequenceGroup sg;
144       if (selection != null && selectedRegionOnly)
145       {
146         // trim annotation to the region being stored.
147         // strip out any groups that do not actually intersect with the
148         // visible and selected region
149         int ssel = selection.getStartRes(), esel = selection.getEndRes();
150         Vector isg = new Vector();
151         Enumeration en = grps.elements();
152         while (en.hasMoreElements())
153         {
154           sg = (SequenceGroup) en.nextElement();
155
156           if (!(sg.getStartRes() > esel || sg.getEndRes() < ssel))
157           {
158             // adjust bounds of new group, if necessary.
159             if (sg.getStartRes() < ssel)
160             {
161               sg.setStartRes(ssel);
162             }
163             if (sg.getEndRes() > esel)
164             {
165               sg.setEndRes(esel);
166             }
167             sg.setStartRes(sg.getStartRes() - ssel + 1);
168             sg.setEndRes(sg.getEndRes() - ssel + 1);
169
170             isg.addElement(sg);
171           }
172         }
173         grps = isg;
174       }
175
176       sgrps = new ScGroup[grps.size()];
177       addedgps = new boolean[grps.size()];
178       for (int g = 0; g < sgrps.length; g++)
179       {
180         sg = (SequenceGroup) grps.elementAt(g);
181         sgrps[g] = new ScGroup();
182         sgrps[g].sg = new SequenceGroup(sg);
183         addedgps[g] = false;
184         grps.setElementAt(sg.getSequences(null), g);
185       }
186       // grps now contains vectors (should be sets) for each group, so we can
187       // track when we've done with the group
188     }
189     int csi = 0;
190     for (int i = 0; i < selseqs.length; i++)
191     {
192       if (selseqs[i] != null)
193       {
194         if (selection != null && selection.getSize() > 0
195                 && !selectedRegionOnly)
196         {
197           sequences[csi].setGroupMembership(selected);
198           selected.addElement(sequences[csi]);
199         }
200         if (grps != null)
201         {
202           for (int sg = 0; sg < sgrps.length; sg++)
203           {
204             if (((Vector) grps.elementAt(sg)).contains(selseqs[i]))
205             {
206               sequences[csi].setGroupMembership(sgrps[sg]);
207               sgrps[sg].sg.deleteSequence(selseqs[i], false);
208               sgrps[sg].seqs.addElement(sequences[csi]);
209               if (!addedgps[sg])
210               {
211                 if (scGroups == null)
212                 {
213                   scGroups = new Vector();
214                 }
215                 addedgps[sg] = true;
216                 scGroups.addElement(sgrps[sg]);
217               }
218             }
219           }
220         }
221         csi++;
222       }
223     }
224     // finally, delete the remaining sequences (if any) not selected
225     for (int sg = 0; sg < sgrps.length; sg++)
226     {
227       SequenceI[] sqs = sgrps[sg].sg.getSequencesAsArray(null);
228       for (int si = 0; si < sqs.length; si++)
229       {
230         sgrps[sg].sg.deleteSequence(sqs[si], false);
231       }
232       sgrps[sg] = null;
233     }
234   }
235
236   /**
237    * construct an alignmentView from a SeqCigarArray. Errors are thrown if the
238    * seqcigararray.isSeqCigarArray() flag is not set.
239    */
240   public AlignmentView(CigarArray seqcigararray)
241   {
242     if (!seqcigararray.isSeqCigarArray())
243     {
244       throw new Error(
245               "Implementation Error - can only make an alignment view from a CigarArray of sequences.");
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("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(
686                         "Mismatch between number of sequences in block "
687                                 + j + " (" + mseq.length
688                                 + ") and the original view ("
689                                 + sequences.length + ")");
690               }
691               swidth = mseq[0].getLength(); // JBPNote: could ensure padded
692               // here.
693               for (int s = 0; s < mseq.length; s++)
694               {
695                 if (alignment[s] == null)
696                 {
697                   alignment[s] = mseq[s];
698                 }
699                 else
700                 {
701                   alignment[s].setSequence(alignment[s]
702                           .getSequenceAsString()
703                           + mseq[s].getSequenceAsString());
704                   if (mseq[s].getStart() <= mseq[s].getEnd())
705                   {
706                     alignment[s].setEnd(mseq[s].getEnd());
707                   }
708                   if (order != null)
709                   {
710                     order.updateSequence(mseq[s], alignment[s]);
711                   }
712                 }
713               }
714             }
715             else
716             {
717               // recover original alignment block or place gaps
718               if (true)
719               {
720                 // recover input data
721                 for (int s = 0; s < sequences.length; s++)
722                 {
723                   SequenceI oseq = sequences[s].getSeq(gapCharacter)
724                           .getSubSequence(start, contigs[contig + 1]);
725                   if (swidth < oseq.getLength())
726                   {
727                     swidth = oseq.getLength();
728                   }
729                   if (alignment[s] == null)
730                   {
731                     alignment[s] = oseq;
732                   }
733                   else
734                   {
735                     alignment[s].setSequence(alignment[s]
736                             .getSequenceAsString()
737                             + oseq.getSequenceAsString());
738                     if (oseq.getEnd() >= oseq.getStart())
739                     {
740                       alignment[s].setEnd(oseq.getEnd());
741                     }
742                   }
743                 }
744
745               }
746               j++;
747             }
748             nwidth += swidth;
749           }
750           // advance to begining of visible region
751           start = contigs[contig + 1] + contigs[contig + 2];
752           // add hidden segment to right of next region
753           for (int s = 0; s < sequences.length; s++)
754           {
755             SequenceI hseq = sequences[s].getSeq(gapCharacter)
756                     .getSubSequence(contigs[contig + 1], start);
757             if (alignment[s] == null)
758             {
759               alignment[s] = hseq;
760             }
761             else
762             {
763               alignment[s].setSequence(alignment[s].getSequenceAsString()
764                       + hseq.getSequenceAsString());
765               if (hseq.getEnd() >= hseq.getStart())
766               {
767                 alignment[s].setEnd(hseq.getEnd());
768               }
769             }
770           }
771           // mark hidden segment as hidden in the new alignment
772           columnselection.hideColumns(nwidth, nwidth + contigs[contig + 2]
773                   - 1);
774           nwidth += contigs[contig + 2];
775         }
776         // Do final segment - if it exists
777         if (j < nvismsa.length)
778         {
779           int swidth = 0;
780           if (nvismsa[j] != null)
781           {
782             SequenceI mseq[] = nvismsa[j];
783             AlignmentOrder order = (orders != null) ? orders[j] : null;
784             swidth = mseq[0].getLength();
785             for (int s = 0; s < mseq.length; s++)
786             {
787               if (alignment[s] == null)
788               {
789                 alignment[s] = mseq[s];
790               }
791               else
792               {
793                 alignment[s].setSequence(alignment[s].getSequenceAsString()
794                         + mseq[s].getSequenceAsString());
795                 if (mseq[s].getEnd() >= mseq[s].getStart())
796                 {
797                   alignment[s].setEnd(mseq[s].getEnd());
798                 }
799                 if (order != null)
800                 {
801                   order.updateSequence(mseq[s], alignment[s]);
802                 }
803               }
804             }
805           }
806           else
807           {
808             if (start < owidth)
809             {
810               // recover input data or place gaps
811               if (true)
812               {
813                 // recover input data
814                 for (int s = 0; s < sequences.length; s++)
815                 {
816                   SequenceI oseq = sequences[s].getSeq(gapCharacter)
817                           .getSubSequence(start, owidth + 1);
818                   if (swidth < oseq.getLength())
819                   {
820                     swidth = oseq.getLength();
821                   }
822                   if (alignment[s] == null)
823                   {
824                     alignment[s] = oseq;
825                   }
826                   else
827                   {
828                     alignment[s].setSequence(alignment[s]
829                             .getSequenceAsString()
830                             + oseq.getSequenceAsString());
831                     if (oseq.getEnd() >= oseq.getStart())
832                     {
833                       alignment[s].setEnd(oseq.getEnd());
834                     }
835                   }
836                 }
837                 nwidth += swidth;
838               }
839               else
840               {
841                 // place gaps.
842                 throw new Error("Padding not yet implemented.");
843               }
844             }
845           }
846         }
847       }
848       return new Object[]
849       { alignment, columnselection };
850     }
851     else
852     {
853       if (nvismsa.length != 1)
854       {
855         throw new Error(
856                 "Mismatch between visible blocks to update and number of contigs in view (contigs=0,blocks="
857                         + nvismsa.length);
858       }
859       if (nvismsa[0] != null)
860       {
861         return new Object[]
862         { nvismsa[0], new ColumnSelection() };
863       }
864       else
865       {
866         return getAlignmentAndColumnSelection(gapCharacter);
867       }
868     }
869   }
870
871   /**
872    * returns simple array of start end positions of visible range on alignment.
873    * vis_start and vis_end are inclusive - use
874    * SequenceI.getSubSequence(vis_start, vis_end+1) to recover visible sequence
875    * from underlying alignment.
876    * 
877    * @return int[] { start_i, end_i } for 1<i<n visible regions.
878    */
879   public int[] getVisibleContigs()
880   {
881     if (contigs != null && contigs.length > 0)
882     {
883       int start = 0;
884       int nvis = 0;
885       int fwidth = width;
886       for (int contig = 0; contig < contigs.length; contig += 3)
887       {
888         if ((contigs[contig + 1] - start) > 0)
889         {
890           nvis++;
891         }
892         fwidth += contigs[contig + 2]; // end up with full region width
893         // (including hidden regions)
894         start = contigs[contig + 1] + contigs[contig + 2];
895       }
896       if (start < fwidth)
897       {
898         nvis++;
899       }
900       int viscontigs[] = new int[nvis * 2];
901       nvis = 0;
902       start = 0;
903       for (int contig = 0; contig < contigs.length; contig += 3)
904       {
905         if ((contigs[contig + 1] - start) > 0)
906         {
907           viscontigs[nvis] = start;
908           viscontigs[nvis + 1] = contigs[contig + 1] - 1; // end is inclusive
909           nvis += 2;
910         }
911         start = contigs[contig + 1] + contigs[contig + 2];
912       }
913       if (start < fwidth)
914       {
915         viscontigs[nvis] = start;
916         viscontigs[nvis + 1] = fwidth; // end is inclusive
917         nvis += 2;
918       }
919       return viscontigs;
920     }
921     else
922     {
923       return new int[]
924       { 0, width };
925     }
926   }
927
928   /**
929    * 
930    * @return position of first visible column of AlignmentView within its
931    *         parent's alignment reference frame
932    */
933   public int getAlignmentOrigin()
934   {
935     return firstCol;
936   }
937
938   /**
939    * compute a deletion map for the current view according to the given
940    * gap/match map
941    * 
942    * @param gapMap
943    *          (as returned from SequenceI.gapMap())
944    * @return int[] {intersection of visible regions with gapMap)
945    */
946   public int[] getVisibleContigMapFor(int[] gapMap)
947   {
948     int[] delMap = null;
949     int[] viscontigs = getVisibleContigs();
950     int spos = 0;
951     int i = 0;
952     if (viscontigs != null)
953     {
954       // viscontigs maps from a subset of the gapMap to the gapMap, so it will
955       // always be equal to or shorter than gapMap
956       delMap = new int[gapMap.length];
957       for (int contig = 0; contig < viscontigs.length; contig += 2)
958       {
959
960         while (spos < gapMap.length && gapMap[spos] < viscontigs[contig])
961         {
962           spos++;
963         }
964         while (spos < gapMap.length
965                 && gapMap[spos] <= viscontigs[contig + 1])
966         {
967           delMap[i++] = spos++;
968         }
969       }
970       int tmap[] = new int[i];
971       System.arraycopy(delMap, 0, tmap, 0, i);
972       delMap = tmap;
973     }
974     return delMap;
975   }
976
977   /**
978    * apply the getSeq(gc) method to each sequence cigar, and return the array of
979    * edited sequences, optionally with hidden regions removed.
980    * 
981    * @param gc
982    *          gap character to use for insertions
983    * @param delete
984    *          remove hidden regions from sequences. Note: currently implemented
985    *          in a memory inefficient way - space needed is 2*result set for
986    *          deletion
987    * 
988    * @return SequenceI[]
989    */
990   public SequenceI[] getEditedSequences(char gc, boolean delete)
991   {
992     SeqCigar[] msf = getSequences();
993     SequenceI[] aln = new SequenceI[msf.length];
994     for (int i = 0, j = msf.length; i < j; i++)
995     {
996       aln[i] = msf[i].getSeq(gc);
997     }
998     if (delete)
999     {
1000       String[] sqs = getSequenceStrings(gc);
1001       for (int i = 0; i < sqs.length; i++)
1002       {
1003         aln[i].setSequence(sqs[i]);
1004         sqs[i] = null;
1005       }
1006     }
1007     return aln;
1008   }
1009
1010   public static void summariseAlignmentView(AlignmentView view,
1011           PrintStream os)
1012   {
1013     os.print("View has " + view.sequences.length + " of which ");
1014     if (view.selected == null)
1015     {
1016       os.print("None");
1017     }
1018     else
1019     {
1020       os.print(" " + view.selected.size());
1021     }
1022     os.println(" are selected.");
1023     os.print("View is " + view.getWidth() + " columns wide");
1024     int viswid = 0;
1025     int[] contigs = view.getContigs();
1026     if (contigs != null)
1027     {
1028       viswid = view.width;
1029       for (int i = 0; i < contigs.length; i += 3)
1030       {
1031         viswid += contigs[i + 2];
1032       }
1033       os.println("with " + viswid + " visible columns spread over "
1034               + contigs.length / 3 + " regions.");
1035     }
1036     else
1037     {
1038       viswid = view.width;
1039       os.println(".");
1040     }
1041     if (view.scGroups != null)
1042     {
1043       os.println("There are " + view.scGroups.size()
1044               + " groups defined on the view.");
1045       for (int g = 0; g < view.scGroups.size(); g++)
1046       {
1047         ScGroup sgr = (ScGroup) view.scGroups.elementAt(g);
1048         os.println("Group " + g + ": Name = " + sgr.sg.getName()
1049                 + " Contains " + sgr.seqs.size() + " Seqs.");
1050         os.println("This group runs from " + sgr.sg.getStartRes() + " to "
1051                 + sgr.sg.getEndRes());
1052         for (int s = 0; s < sgr.seqs.size(); s++)
1053         {
1054           if (!((SeqCigar) sgr.seqs.elementAt(s)).isMemberOf(sgr))
1055           {
1056             os.println("** WARNING: sequence "
1057                     + ((SeqCigar) sgr.seqs.elementAt(s)).toString()
1058                     + " is not marked as member of group.");
1059           }
1060         }
1061       }
1062       AlignmentI visal = view.getVisibleAlignment('-');
1063       if (visal != null)
1064       {
1065         os.println("Vis. alignment is " + visal.getWidth()
1066                 + " wide and has " + visal.getHeight() + " seqs.");
1067         if (visal.getGroups() != null && visal.getGroups().size() > 0)
1068         {
1069
1070           int i = 1;
1071           for (SequenceGroup sg : visal.getGroups())
1072           {
1073             os.println("Group " + (i++) + " begins at column "
1074                     + sg.getStartRes() + " and ends at " + sg.getEndRes());
1075           }
1076         }
1077       }
1078     }
1079   }
1080
1081   public static void testSelectionViews(AlignmentI alignment,
1082           ColumnSelection csel, SequenceGroup selection)
1083   {
1084     System.out.println("Testing standard view creation:\n");
1085     AlignmentView view = null;
1086     try
1087     {
1088       System.out
1089               .println("View with no hidden columns, no limit to selection, no groups to be collected:");
1090       view = new AlignmentView(alignment, csel, selection, false, false,
1091               false);
1092       summariseAlignmentView(view, System.out);
1093
1094     } catch (Exception e)
1095     {
1096       e.printStackTrace();
1097       System.err
1098               .println("Failed to generate alignment with selection but no groups marked.");
1099     }
1100     try
1101     {
1102       System.out
1103               .println("View with no hidden columns, no limit to selection, and all groups to be collected:");
1104       view = new AlignmentView(alignment, csel, selection, false, false,
1105               true);
1106       summariseAlignmentView(view, System.out);
1107     } catch (Exception e)
1108     {
1109       e.printStackTrace();
1110       System.err
1111               .println("Failed to generate alignment with selection marked but no groups marked.");
1112     }
1113     try
1114     {
1115       System.out
1116               .println("View with no hidden columns, limited to selection and no groups to be collected:");
1117       view = new AlignmentView(alignment, csel, selection, false, true,
1118               false);
1119       summariseAlignmentView(view, System.out);
1120     } catch (Exception e)
1121     {
1122       e.printStackTrace();
1123       System.err
1124               .println("Failed to generate alignment with selection restricted but no groups marked.");
1125     }
1126     try
1127     {
1128       System.out
1129               .println("View with no hidden columns, limited to selection, and all groups to be collected:");
1130       view = new AlignmentView(alignment, csel, selection, false, true,
1131               true);
1132       summariseAlignmentView(view, System.out);
1133     } catch (Exception e)
1134     {
1135       e.printStackTrace();
1136       System.err
1137               .println("Failed to generate alignment with selection restricted and groups marked.");
1138     }
1139     try
1140     {
1141       System.out
1142               .println("View *with* hidden columns, no limit to selection, no groups to be collected:");
1143       view = new AlignmentView(alignment, csel, selection, true, false,
1144               false);
1145       summariseAlignmentView(view, System.out);
1146     } catch (Exception e)
1147     {
1148       e.printStackTrace();
1149       System.err
1150               .println("Failed to generate alignment with selection but no groups marked.");
1151     }
1152     try
1153     {
1154       System.out
1155               .println("View *with* hidden columns, no limit to selection, and all groups to be collected:");
1156       view = new AlignmentView(alignment, csel, selection, true, false,
1157               true);
1158       summariseAlignmentView(view, System.out);
1159     } catch (Exception e)
1160     {
1161       e.printStackTrace();
1162       System.err
1163               .println("Failed to generate alignment with selection marked but no groups marked.");
1164     }
1165     try
1166     {
1167       System.out
1168               .println("View *with* hidden columns, limited to selection and no groups to be collected:");
1169       view = new AlignmentView(alignment, csel, selection, true, true,
1170               false);
1171       summariseAlignmentView(view, System.out);
1172     } catch (Exception e)
1173     {
1174       e.printStackTrace();
1175       System.err
1176               .println("Failed to generate alignment with selection restricted but no groups marked.");
1177     }
1178     try
1179     {
1180       System.out
1181               .println("View *with* hidden columns, limited to selection, and all groups to be collected:");
1182       view = new AlignmentView(alignment, csel, selection, true, true, true);
1183       summariseAlignmentView(view, System.out);
1184     } catch (Exception e)
1185     {
1186       e.printStackTrace();
1187       System.err
1188               .println("Failed to generate alignment with selection restricted and groups marked.");
1189     }
1190
1191   }
1192 }