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