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