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