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