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