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