JAL-1834 removed quotes around truncated strings with ellipsis
[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    * @return
469    */
470   private SequenceI[] getVisibleSeqs(char c)
471   {
472     SequenceI[] aln = new SequenceI[sequences.length];
473     for (int i = 0, j = sequences.length; i < j; i++)
474     {
475       aln[i] = sequences[i].getSeq('-');
476     }
477     // Remove hidden regions from sequence objects.
478     String seqs[] = getSequenceStrings('-');
479     for (int i = 0, j = aln.length; i < j; i++)
480     {
481       aln[i].setSequence(seqs[i]);
482     }
483     return aln;
484   }
485
486   /**
487    * creates new alignment objects for all contiguous visible segments
488    * 
489    * @param c
490    * @param start
491    * @param end
492    * @param regionOfInterest
493    *          specify which sequences to include (or null to include all
494    *          sequences)
495    * @return AlignmentI[] - all alignments where each sequence is a subsequence
496    *         constructed from visible contig regions of view
497    */
498   public AlignmentI[] getVisibleContigAlignments(char c)
499   {
500     int nvc = 0;
501     int[] vcontigs = getVisibleContigs();
502     SequenceI[][] contigviews = getVisibleContigs(c);
503     AlignmentI[] vcals = new AlignmentI[contigviews.length];
504     for (nvc = 0; nvc < contigviews.length; nvc++)
505     {
506       vcals[nvc] = new Alignment(contigviews[nvc]);
507       if (scGroups != null && scGroups.size() > 0)
508       {
509         addPrunedGroupsInOrder(vcals[nvc], vcontigs[nvc * 2],
510                 vcontigs[nvc * 2 + 1], true);
511       }
512     }
513     return vcals;
514   }
515
516   /**
517    * get an array of visible sequence strings for a view on an alignment using
518    * the given gap character
519    * 
520    * @param c
521    *          char
522    * @return String[]
523    */
524   public String[] getSequenceStrings(char c)
525   {
526     String[] seqs = new String[sequences.length];
527     for (int n = 0; n < sequences.length; n++)
528     {
529       String fullseq = sequences[n].getSequenceString(c);
530       if (contigs != null)
531       {
532         seqs[n] = "";
533         int p = 0;
534         for (int h = 0; h < contigs.length; h += 3)
535         {
536           seqs[n] += fullseq.substring(p, contigs[h + 1]);
537           p = contigs[h + 1] + contigs[h + 2];
538         }
539         seqs[n] += fullseq.substring(p);
540       }
541       else
542       {
543         seqs[n] = fullseq;
544       }
545     }
546     return seqs;
547   }
548
549   /**
550    * 
551    * @return visible number of columns in alignment view
552    */
553   public int getWidth()
554   {
555     return width;
556   }
557
558   protected void setWidth(int width)
559   {
560     this.width = width;
561   }
562
563   /**
564    * get the contiguous subalignments in an alignment view.
565    * 
566    * @param gapCharacter
567    *          char
568    * @return SequenceI[][]
569    */
570   public SequenceI[][] getVisibleContigs(char gapCharacter)
571   {
572     SequenceI[][] smsa;
573     int njobs = 1;
574     if (sequences == null || width <= 0)
575     {
576       return null;
577     }
578     if (contigs != null && contigs.length > 0)
579     {
580       int start = 0;
581       njobs = 0;
582       int fwidth = width;
583       for (int contig = 0; contig < contigs.length; contig += 3)
584       {
585         if ((contigs[contig + 1] - start) > 0)
586         {
587           njobs++;
588         }
589         fwidth += contigs[contig + 2]; // end up with full region width
590         // (including hidden regions)
591         start = contigs[contig + 1] + contigs[contig + 2];
592       }
593       if (start < fwidth)
594       {
595         njobs++;
596       }
597       smsa = new SequenceI[njobs][];
598       start = 0;
599       int j = 0;
600       for (int contig = 0; contig < contigs.length; contig += 3)
601       {
602         if (contigs[contig + 1] - start > 0)
603         {
604           SequenceI mseq[] = new SequenceI[sequences.length];
605           for (int s = 0; s < mseq.length; s++)
606           {
607             mseq[s] = sequences[s].getSeq(gapCharacter).getSubSequence(
608                     start, contigs[contig + 1]);
609           }
610           smsa[j] = mseq;
611           j++;
612         }
613         start = contigs[contig + 1] + contigs[contig + 2];
614       }
615       if (start < fwidth)
616       {
617         SequenceI mseq[] = new SequenceI[sequences.length];
618         for (int s = 0; s < mseq.length; s++)
619         {
620           mseq[s] = sequences[s].getSeq(gapCharacter).getSubSequence(start,
621                   fwidth + 1);
622         }
623         smsa[j] = mseq;
624         j++;
625       }
626     }
627     else
628     {
629       smsa = new SequenceI[1][];
630       smsa[0] = new SequenceI[sequences.length];
631       for (int s = 0; s < sequences.length; s++)
632       {
633         smsa[0][s] = sequences[s].getSeq(gapCharacter);
634       }
635     }
636     return smsa;
637   }
638
639   /**
640    * return full msa and hidden regions with visible blocks replaced with new
641    * sub alignments
642    * 
643    * @param nvismsa
644    *          SequenceI[][]
645    * @param orders
646    *          AlignmentOrder[] corresponding to each SequenceI[] block.
647    * @return Object[]
648    */
649   public Object[] getUpdatedView(SequenceI[][] nvismsa,
650           AlignmentOrder[] orders, char gapCharacter)
651   {
652     if (sequences == null || width <= 0)
653     {
654       throw new Error(MessageManager.getString("error.empty_view_cannot_be_updated"));
655     }
656     if (nvismsa == null)
657     {
658       throw new Error(
659               "nvismsa==null. use getAlignmentAndColumnSelection() instead.");
660     }
661     if (contigs != null && contigs.length > 0)
662     {
663       SequenceI[] alignment = new SequenceI[sequences.length];
664       ColumnSelection columnselection = new ColumnSelection();
665       if (contigs != null && contigs.length > 0)
666       {
667         int start = 0;
668         int nwidth = 0;
669         int owidth = width;
670         int j = 0;
671         for (int contig = 0; contig < contigs.length; contig += 3)
672         {
673           owidth += contigs[contig + 2]; // recover final column width
674           if (contigs[contig + 1] - start > 0)
675           {
676             int swidth = 0; // subalignment width
677             if (nvismsa[j] != null)
678             {
679               SequenceI mseq[] = nvismsa[j];
680               AlignmentOrder order = (orders == null) ? null : orders[j];
681               j++;
682               if (mseq.length != sequences.length)
683               {
684                 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() }));
685               }
686               swidth = mseq[0].getLength(); // JBPNote: could ensure padded
687               // here.
688               for (int s = 0; s < mseq.length; s++)
689               {
690                 if (alignment[s] == null)
691                 {
692                   alignment[s] = mseq[s];
693                 }
694                 else
695                 {
696                   alignment[s].setSequence(alignment[s]
697                           .getSequenceAsString()
698                           + mseq[s].getSequenceAsString());
699                   if (mseq[s].getStart() <= mseq[s].getEnd())
700                   {
701                     alignment[s].setEnd(mseq[s].getEnd());
702                   }
703                   if (order != null)
704                   {
705                     order.updateSequence(mseq[s], alignment[s]);
706                   }
707                 }
708               }
709             }
710             else
711             {
712               // recover original alignment block or place gaps
713               if (true)
714               {
715                 // recover input data
716                 for (int s = 0; s < sequences.length; s++)
717                 {
718                   SequenceI oseq = sequences[s].getSeq(gapCharacter)
719                           .getSubSequence(start, contigs[contig + 1]);
720                   if (swidth < oseq.getLength())
721                   {
722                     swidth = oseq.getLength();
723                   }
724                   if (alignment[s] == null)
725                   {
726                     alignment[s] = oseq;
727                   }
728                   else
729                   {
730                     alignment[s].setSequence(alignment[s]
731                             .getSequenceAsString()
732                             + oseq.getSequenceAsString());
733                     if (oseq.getEnd() >= oseq.getStart())
734                     {
735                       alignment[s].setEnd(oseq.getEnd());
736                     }
737                   }
738                 }
739
740               }
741               j++;
742             }
743             nwidth += swidth;
744           }
745           // advance to begining of visible region
746           start = contigs[contig + 1] + contigs[contig + 2];
747           // add hidden segment to right of next region
748           for (int s = 0; s < sequences.length; s++)
749           {
750             SequenceI hseq = sequences[s].getSeq(gapCharacter)
751                     .getSubSequence(contigs[contig + 1], start);
752             if (alignment[s] == null)
753             {
754               alignment[s] = hseq;
755             }
756             else
757             {
758               alignment[s].setSequence(alignment[s].getSequenceAsString()
759                       + hseq.getSequenceAsString());
760               if (hseq.getEnd() >= hseq.getStart())
761               {
762                 alignment[s].setEnd(hseq.getEnd());
763               }
764             }
765           }
766           // mark hidden segment as hidden in the new alignment
767           columnselection.hideColumns(nwidth, nwidth + contigs[contig + 2]
768                   - 1);
769           nwidth += contigs[contig + 2];
770         }
771         // Do final segment - if it exists
772         if (j < nvismsa.length)
773         {
774           int swidth = 0;
775           if (nvismsa[j] != null)
776           {
777             SequenceI mseq[] = nvismsa[j];
778             AlignmentOrder order = (orders != null) ? orders[j] : null;
779             swidth = mseq[0].getLength();
780             for (int s = 0; s < mseq.length; s++)
781             {
782               if (alignment[s] == null)
783               {
784                 alignment[s] = mseq[s];
785               }
786               else
787               {
788                 alignment[s].setSequence(alignment[s].getSequenceAsString()
789                         + mseq[s].getSequenceAsString());
790                 if (mseq[s].getEnd() >= mseq[s].getStart())
791                 {
792                   alignment[s].setEnd(mseq[s].getEnd());
793                 }
794                 if (order != null)
795                 {
796                   order.updateSequence(mseq[s], alignment[s]);
797                 }
798               }
799             }
800           }
801           else
802           {
803             if (start < owidth)
804             {
805               // recover input data or place gaps
806               if (true)
807               {
808                 // recover input data
809                 for (int s = 0; s < sequences.length; s++)
810                 {
811                   SequenceI oseq = sequences[s].getSeq(gapCharacter)
812                           .getSubSequence(start, owidth + 1);
813                   if (swidth < oseq.getLength())
814                   {
815                     swidth = oseq.getLength();
816                   }
817                   if (alignment[s] == null)
818                   {
819                     alignment[s] = oseq;
820                   }
821                   else
822                   {
823                     alignment[s].setSequence(alignment[s]
824                             .getSequenceAsString()
825                             + oseq.getSequenceAsString());
826                     if (oseq.getEnd() >= oseq.getStart())
827                     {
828                       alignment[s].setEnd(oseq.getEnd());
829                     }
830                   }
831                 }
832                 nwidth += swidth;
833               }
834               else
835               {
836                 // place gaps.
837                 throw new Error(MessageManager.getString("error.padding_not_yet_implemented"));
838               }
839             }
840           }
841         }
842       }
843       return new Object[]
844       { alignment, columnselection };
845     }
846     else
847     {
848       if (nvismsa.length != 1)
849       {
850         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()}));
851       }
852       if (nvismsa[0] != null)
853       {
854         return new Object[]
855         { nvismsa[0], new ColumnSelection() };
856       }
857       else
858       {
859         return getAlignmentAndColumnSelection(gapCharacter);
860       }
861     }
862   }
863
864   /**
865    * returns simple array of start end positions of visible range on alignment.
866    * vis_start and vis_end are inclusive - use
867    * SequenceI.getSubSequence(vis_start, vis_end+1) to recover visible sequence
868    * from underlying alignment.
869    * 
870    * @return int[] { start_i, end_i } for 1<i<n visible regions.
871    */
872   public int[] getVisibleContigs()
873   {
874     if (contigs != null && contigs.length > 0)
875     {
876       int start = 0;
877       int nvis = 0;
878       int fwidth = width;
879       for (int contig = 0; contig < contigs.length; contig += 3)
880       {
881         if ((contigs[contig + 1] - start) > 0)
882         {
883           nvis++;
884         }
885         fwidth += contigs[contig + 2]; // end up with full region width
886         // (including hidden regions)
887         start = contigs[contig + 1] + contigs[contig + 2];
888       }
889       if (start < fwidth)
890       {
891         nvis++;
892       }
893       int viscontigs[] = new int[nvis * 2];
894       nvis = 0;
895       start = 0;
896       for (int contig = 0; contig < contigs.length; contig += 3)
897       {
898         if ((contigs[contig + 1] - start) > 0)
899         {
900           viscontigs[nvis] = start;
901           viscontigs[nvis + 1] = contigs[contig + 1] - 1; // end is inclusive
902           nvis += 2;
903         }
904         start = contigs[contig + 1] + contigs[contig + 2];
905       }
906       if (start < fwidth)
907       {
908         viscontigs[nvis] = start;
909         viscontigs[nvis + 1] = fwidth; // end is inclusive
910         nvis += 2;
911       }
912       return viscontigs;
913     }
914     else
915     {
916       return new int[]
917       { 0, width };
918     }
919   }
920
921   /**
922    * 
923    * @return position of first visible column of AlignmentView within its
924    *         parent's alignment reference frame
925    */
926   public int getAlignmentOrigin()
927   {
928     return firstCol;
929   }
930
931   /**
932    * compute a deletion map for the current view according to the given
933    * gap/match map
934    * 
935    * @param gapMap
936    *          (as returned from SequenceI.gapMap())
937    * @return int[] {intersection of visible regions with gapMap)
938    */
939   public int[] getVisibleContigMapFor(int[] gapMap)
940   {
941     int[] delMap = null;
942     int[] viscontigs = getVisibleContigs();
943     int spos = 0;
944     int i = 0;
945     if (viscontigs != null)
946     {
947       // viscontigs maps from a subset of the gapMap to the gapMap, so it will
948       // always be equal to or shorter than gapMap
949       delMap = new int[gapMap.length];
950       for (int contig = 0; contig < viscontigs.length; contig += 2)
951       {
952
953         while (spos < gapMap.length && gapMap[spos] < viscontigs[contig])
954         {
955           spos++;
956         }
957         while (spos < gapMap.length
958                 && gapMap[spos] <= viscontigs[contig + 1])
959         {
960           delMap[i++] = spos++;
961         }
962       }
963       int tmap[] = new int[i];
964       System.arraycopy(delMap, 0, tmap, 0, i);
965       delMap = tmap;
966     }
967     return delMap;
968   }
969
970   /**
971    * apply the getSeq(gc) method to each sequence cigar, and return the array of
972    * edited sequences, optionally with hidden regions removed.
973    * 
974    * @param gc
975    *          gap character to use for insertions
976    * @param delete
977    *          remove hidden regions from sequences. Note: currently implemented
978    *          in a memory inefficient way - space needed is 2*result set for
979    *          deletion
980    * 
981    * @return SequenceI[]
982    */
983   public SequenceI[] getEditedSequences(char gc, boolean delete)
984   {
985     SeqCigar[] msf = getSequences();
986     SequenceI[] aln = new SequenceI[msf.length];
987     for (int i = 0, j = msf.length; i < j; i++)
988     {
989       aln[i] = msf[i].getSeq(gc);
990     }
991     if (delete)
992     {
993       String[] sqs = getSequenceStrings(gc);
994       for (int i = 0; i < sqs.length; i++)
995       {
996         aln[i].setSequence(sqs[i]);
997         sqs[i] = null;
998       }
999     }
1000     return aln;
1001   }
1002
1003   public static void summariseAlignmentView(AlignmentView view,
1004           PrintStream os)
1005   {
1006     os.print("View has " + view.sequences.length + " of which ");
1007     if (view.selected == null)
1008     {
1009       os.print("None");
1010     }
1011     else
1012     {
1013       os.print(" " + view.selected.size());
1014     }
1015     os.println(" are selected.");
1016     os.print("View is " + view.getWidth() + " columns wide");
1017     int viswid = 0;
1018     int[] contigs = view.getContigs();
1019     if (contigs != null)
1020     {
1021       viswid = view.width;
1022       for (int i = 0; i < contigs.length; i += 3)
1023       {
1024         viswid += contigs[i + 2];
1025       }
1026       os.println("with " + viswid + " visible columns spread over "
1027               + contigs.length / 3 + " regions.");
1028     }
1029     else
1030     {
1031       viswid = view.width;
1032       os.println(".");
1033     }
1034     if (view.scGroups != null)
1035     {
1036       os.println("There are " + view.scGroups.size()
1037               + " groups defined on the view.");
1038       for (int g = 0; g < view.scGroups.size(); g++)
1039       {
1040         ScGroup sgr = view.scGroups.get(g);
1041         os.println("Group " + g + ": Name = " + sgr.sg.getName()
1042                 + " Contains " + sgr.seqs.size() + " Seqs.");
1043         os.println("This group runs from " + sgr.sg.getStartRes() + " to "
1044                 + sgr.sg.getEndRes());
1045         for (int s = 0; s < sgr.seqs.size(); s++)
1046         {
1047           if (!((SeqCigar) sgr.seqs.elementAt(s)).isMemberOf(sgr))
1048           {
1049             os.println("** WARNING: sequence "
1050                     + ((SeqCigar) sgr.seqs.elementAt(s)).toString()
1051                     + " is not marked as member of group.");
1052           }
1053         }
1054       }
1055       AlignmentI visal = view.getVisibleAlignment('-');
1056       if (visal != null)
1057       {
1058         os.println("Vis. alignment is " + visal.getWidth()
1059                 + " wide and has " + visal.getHeight() + " seqs.");
1060         if (visal.getGroups() != null && visal.getGroups().size() > 0)
1061         {
1062
1063           int i = 1;
1064           for (SequenceGroup sg : visal.getGroups())
1065           {
1066             os.println("Group " + (i++) + " begins at column "
1067                     + sg.getStartRes() + " and ends at " + sg.getEndRes());
1068           }
1069         }
1070       }
1071     }
1072   }
1073
1074   public static void testSelectionViews(AlignmentI alignment,
1075           ColumnSelection csel, SequenceGroup selection)
1076   {
1077     System.out.println("Testing standard view creation:\n");
1078     AlignmentView view = null;
1079     try
1080     {
1081       System.out
1082               .println("View with no hidden columns, no limit to selection, no groups to be collected:");
1083       view = new AlignmentView(alignment, csel, selection, false, false,
1084               false);
1085       summariseAlignmentView(view, System.out);
1086
1087     } catch (Exception e)
1088     {
1089       e.printStackTrace();
1090       System.err
1091               .println("Failed to generate alignment with selection but no groups marked.");
1092     }
1093     try
1094     {
1095       System.out
1096               .println("View with no hidden columns, no limit to selection, and all groups to be collected:");
1097       view = new AlignmentView(alignment, csel, selection, false, false,
1098               true);
1099       summariseAlignmentView(view, System.out);
1100     } catch (Exception e)
1101     {
1102       e.printStackTrace();
1103       System.err
1104               .println("Failed to generate alignment with selection marked but no groups marked.");
1105     }
1106     try
1107     {
1108       System.out
1109               .println("View with no hidden columns, limited to selection and no groups to be collected:");
1110       view = new AlignmentView(alignment, csel, selection, false, true,
1111               false);
1112       summariseAlignmentView(view, System.out);
1113     } catch (Exception e)
1114     {
1115       e.printStackTrace();
1116       System.err
1117               .println("Failed to generate alignment with selection restricted but no groups marked.");
1118     }
1119     try
1120     {
1121       System.out
1122               .println("View with no hidden columns, limited to selection, and all groups to be collected:");
1123       view = new AlignmentView(alignment, csel, selection, false, true,
1124               true);
1125       summariseAlignmentView(view, System.out);
1126     } catch (Exception e)
1127     {
1128       e.printStackTrace();
1129       System.err
1130               .println("Failed to generate alignment with selection restricted and groups marked.");
1131     }
1132     try
1133     {
1134       System.out
1135               .println("View *with* hidden columns, no limit to selection, no groups to be collected:");
1136       view = new AlignmentView(alignment, csel, selection, true, false,
1137               false);
1138       summariseAlignmentView(view, System.out);
1139     } catch (Exception e)
1140     {
1141       e.printStackTrace();
1142       System.err
1143               .println("Failed to generate alignment with selection but no groups marked.");
1144     }
1145     try
1146     {
1147       System.out
1148               .println("View *with* hidden columns, no limit to selection, and all groups to be collected:");
1149       view = new AlignmentView(alignment, csel, selection, true, false,
1150               true);
1151       summariseAlignmentView(view, System.out);
1152     } catch (Exception e)
1153     {
1154       e.printStackTrace();
1155       System.err
1156               .println("Failed to generate alignment with selection marked but no groups marked.");
1157     }
1158     try
1159     {
1160       System.out
1161               .println("View *with* hidden columns, limited to selection and no groups to be collected:");
1162       view = new AlignmentView(alignment, csel, selection, true, true,
1163               false);
1164       summariseAlignmentView(view, System.out);
1165     } catch (Exception e)
1166     {
1167       e.printStackTrace();
1168       System.err
1169               .println("Failed to generate alignment with selection restricted but no groups marked.");
1170     }
1171     try
1172     {
1173       System.out
1174               .println("View *with* hidden columns, limited to selection, and all groups to be collected:");
1175       view = new AlignmentView(alignment, csel, selection, true, true, true);
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 and groups marked.");
1182     }
1183
1184   }
1185 }