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