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