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