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