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