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