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