0884ea49d74ce082fc545f6a25277df0ab6ef010
[jalview.git] / src / jalview / datamodel / AlignmentView.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.4)\r
3  * Copyright (C) 2008 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
4  * \r
5  * This program is free software; you can redistribute it and/or\r
6  * modify it under the terms of the GNU General Public License\r
7  * as published by the Free Software Foundation; either version 2\r
8  * of the License, or (at your option) any later version.\r
9  * \r
10  * This program is distributed in the hope that it will be useful,\r
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13  * GNU General Public License for more details.\r
14  * \r
15  * You should have received a copy of the GNU General Public License\r
16  * along with this program; if not, write to the Free Software\r
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
18  */\r
19 package jalview.datamodel;\r
20 \r
21 /**\r
22  * <p>\r
23  * Title:\r
24  * </p>\r
25  * \r
26  * <p>\r
27  * Description:\r
28  * </p>\r
29  * \r
30  * <p>\r
31  * Copyright: Copyright (c) 2004\r
32  * </p>\r
33  * \r
34  * <p>\r
35  * Company: Dundee University\r
36  * </p>\r
37  * \r
38  * @author not attributable\r
39  * @version 1.0\r
40  */\r
41 public class AlignmentView\r
42 {\r
43   /**\r
44    * Transient object compactly representing a 'view' of an alignment - with\r
45    * discontinuities marked.\r
46    */\r
47   private SeqCigar[] sequences = null;\r
48 \r
49   private int[] contigs = null;\r
50 \r
51   private int width = 0;\r
52 \r
53   private int firstCol = 0;\r
54 \r
55   public AlignmentView(CigarArray seqcigararray)\r
56   {\r
57     if (!seqcigararray.isSeqCigarArray())\r
58     {\r
59       throw new Error(\r
60               "Implementation Error - can only make an alignment view from a CigarArray of sequences.");\r
61     }\r
62     // contigs = seqcigararray.applyDeletions();\r
63     contigs = seqcigararray.getDeletedRegions();\r
64     sequences = seqcigararray.getSeqCigarArray();\r
65     width = seqcigararray.getWidth(); // visible width\r
66   }\r
67 \r
68   /**\r
69    * Create an alignmentView where the first column corresponds with the\r
70    * 'firstcol' column of some reference alignment\r
71    * \r
72    * @param sdata\r
73    * @param firstcol\r
74    */\r
75   public AlignmentView(CigarArray sdata, int firstcol)\r
76   {\r
77     this(sdata);\r
78     firstCol = firstcol;\r
79   }\r
80 \r
81   public void setSequences(SeqCigar[] sequences)\r
82   {\r
83     this.sequences = sequences;\r
84   }\r
85 \r
86   public void setContigs(int[] contigs)\r
87   {\r
88     this.contigs = contigs;\r
89   }\r
90 \r
91   public SeqCigar[] getSequences()\r
92   {\r
93     return sequences;\r
94   }\r
95 \r
96   /**\r
97    * @see CigarArray.getDeletedRegions\r
98    * @return int[] { vis_start, sym_start, length }\r
99    */\r
100   public int[] getContigs()\r
101   {\r
102     return contigs;\r
103   }\r
104 \r
105   /**\r
106    * get the full alignment and a columnselection object marking the hidden\r
107    * regions\r
108    * \r
109    * @param gapCharacter\r
110    *                char\r
111    * @return Object[] { SequenceI[], ColumnSelection}\r
112    */\r
113   public Object[] getAlignmentAndColumnSelection(char gapCharacter)\r
114   {\r
115     ColumnSelection colsel = new ColumnSelection();\r
116 \r
117     return new Object[]\r
118     {\r
119         SeqCigar.createAlignmentSequences(sequences, gapCharacter, colsel,\r
120                 contigs), colsel };\r
121   }\r
122 \r
123   /**\r
124    * getSequenceStrings\r
125    * \r
126    * @param c\r
127    *                char\r
128    * @return String[]\r
129    */\r
130   public String[] getSequenceStrings(char c)\r
131   {\r
132     String[] seqs = new String[sequences.length];\r
133     for (int n = 0; n < sequences.length; n++)\r
134     {\r
135       String fullseq = sequences[n].getSequenceString(c);\r
136       if (contigs != null)\r
137       {\r
138         seqs[n] = "";\r
139         int p = 0;\r
140         for (int h = 0; h < contigs.length; h += 3)\r
141         {\r
142           seqs[n] += fullseq.substring(p, contigs[h + 1]);\r
143           p = contigs[h + 1] + contigs[h + 2];\r
144         }\r
145         seqs[n] += fullseq.substring(p);\r
146       }\r
147       else\r
148       {\r
149         seqs[n] = fullseq;\r
150       }\r
151     }\r
152     return seqs;\r
153   }\r
154 \r
155   /**\r
156    * \r
157    * @return visible number of columns in alignment view\r
158    */\r
159   public int getWidth()\r
160   {\r
161     return width;\r
162   }\r
163 \r
164   protected void setWidth(int width)\r
165   {\r
166     this.width = width;\r
167   }\r
168 \r
169   /**\r
170    * get the contiguous subalignments in an alignment view.\r
171    * \r
172    * @param gapCharacter\r
173    *                char\r
174    * @return SequenceI[][]\r
175    */\r
176   public SequenceI[][] getVisibleContigs(char gapCharacter)\r
177   {\r
178     SequenceI[][] smsa;\r
179     int njobs = 1;\r
180     if (sequences == null || width <= 0)\r
181     {\r
182       return null;\r
183     }\r
184     if (contigs != null && contigs.length > 0)\r
185     {\r
186       int start = 0;\r
187       njobs = 0;\r
188       int fwidth = width;\r
189       for (int contig = 0; contig < contigs.length; contig += 3)\r
190       {\r
191         if ((contigs[contig + 1] - start) > 0)\r
192         {\r
193           njobs++;\r
194         }\r
195         fwidth += contigs[contig + 2]; // end up with full region width\r
196                                         // (including hidden regions)\r
197         start = contigs[contig + 1] + contigs[contig + 2];\r
198       }\r
199       if (start < fwidth)\r
200       {\r
201         njobs++;\r
202       }\r
203       smsa = new SequenceI[njobs][];\r
204       start = 0;\r
205       int j = 0;\r
206       for (int contig = 0; contig < contigs.length; contig += 3)\r
207       {\r
208         if (contigs[contig + 1] - start > 0)\r
209         {\r
210           SequenceI mseq[] = new SequenceI[sequences.length];\r
211           for (int s = 0; s < mseq.length; s++)\r
212           {\r
213             mseq[s] = sequences[s].getSeq(gapCharacter).getSubSequence(\r
214                     start, contigs[contig + 1]);\r
215           }\r
216           smsa[j] = mseq;\r
217           j++;\r
218         }\r
219         start = contigs[contig + 1] + contigs[contig + 2];\r
220       }\r
221       if (start < fwidth)\r
222       {\r
223         SequenceI mseq[] = new SequenceI[sequences.length];\r
224         for (int s = 0; s < mseq.length; s++)\r
225         {\r
226           mseq[s] = sequences[s].getSeq(gapCharacter).getSubSequence(start,\r
227                   fwidth + 1);\r
228         }\r
229         smsa[j] = mseq;\r
230         j++;\r
231       }\r
232     }\r
233     else\r
234     {\r
235       smsa = new SequenceI[1][];\r
236       smsa[0] = new SequenceI[sequences.length];\r
237       for (int s = 0; s < sequences.length; s++)\r
238       {\r
239         smsa[0][s] = sequences[s].getSeq(gapCharacter);\r
240       }\r
241     }\r
242     return smsa;\r
243   }\r
244 \r
245   /**\r
246    * return full msa and hidden regions with visible blocks replaced with new\r
247    * sub alignments\r
248    * \r
249    * @param nvismsa\r
250    *                SequenceI[][]\r
251    * @param orders\r
252    *                AlignmentOrder[] corresponding to each SequenceI[] block.\r
253    * @return Object[]\r
254    */\r
255   public Object[] getUpdatedView(SequenceI[][] nvismsa,\r
256           AlignmentOrder[] orders, char gapCharacter)\r
257   {\r
258     if (sequences == null || width <= 0)\r
259     {\r
260       throw new Error("empty view cannot be updated.");\r
261     }\r
262     if (nvismsa == null)\r
263     {\r
264       throw new Error(\r
265               "nvismsa==null. use getAlignmentAndColumnSelection() instead.");\r
266     }\r
267     if (contigs != null && contigs.length > 0)\r
268     {\r
269       SequenceI[] alignment = new SequenceI[sequences.length];\r
270       ColumnSelection columnselection = new ColumnSelection();\r
271       if (contigs != null && contigs.length > 0)\r
272       {\r
273         int start = 0;\r
274         int nwidth = 0;\r
275         int owidth = width;\r
276         int j = 0;\r
277         for (int contig = 0; contig < contigs.length; contig += 3)\r
278         {\r
279           owidth += contigs[contig + 2]; // recover final column width\r
280           if (contigs[contig + 1] - start > 0)\r
281           {\r
282             int swidth = 0; // subalignment width\r
283             if (nvismsa[j] != null)\r
284             {\r
285               SequenceI mseq[] = nvismsa[j];\r
286               AlignmentOrder order = (orders == null) ? null : orders[j];\r
287               j++;\r
288               if (mseq.length != sequences.length)\r
289               {\r
290                 throw new Error(\r
291                         "Mismatch between number of sequences in block "\r
292                                 + j + " (" + mseq.length\r
293                                 + ") and the original view ("\r
294                                 + sequences.length + ")");\r
295               }\r
296               swidth = mseq[0].getLength(); // JBPNote: could ensure padded\r
297                                             // here.\r
298               for (int s = 0; s < mseq.length; s++)\r
299               {\r
300                 if (alignment[s] == null)\r
301                 {\r
302                   alignment[s] = mseq[s];\r
303                 }\r
304                 else\r
305                 {\r
306                   alignment[s].setSequence(alignment[s]\r
307                           .getSequenceAsString()\r
308                           + mseq[s].getSequenceAsString());\r
309                   if (mseq[s].getStart() <= mseq[s].getEnd())\r
310                   {\r
311                     alignment[s].setEnd(mseq[s].getEnd());\r
312                   }\r
313                   if (order != null)\r
314                   {\r
315                     order.updateSequence(mseq[s], alignment[s]);\r
316                   }\r
317                 }\r
318               }\r
319             }\r
320             else\r
321             {\r
322               // recover original alignment block or place gaps\r
323               if (true)\r
324               {\r
325                 // recover input data\r
326                 for (int s = 0; s < sequences.length; s++)\r
327                 {\r
328                   SequenceI oseq = sequences[s].getSeq(gapCharacter)\r
329                           .getSubSequence(start, contigs[contig + 1]);\r
330                   if (swidth < oseq.getLength())\r
331                   {\r
332                     swidth = oseq.getLength();\r
333                   }\r
334                   if (alignment[s] == null)\r
335                   {\r
336                     alignment[s] = oseq;\r
337                   }\r
338                   else\r
339                   {\r
340                     alignment[s].setSequence(alignment[s]\r
341                             .getSequenceAsString()\r
342                             + oseq.getSequenceAsString());\r
343                     if (oseq.getEnd() >= oseq.getStart())\r
344                     {\r
345                       alignment[s].setEnd(oseq.getEnd());\r
346                     }\r
347                   }\r
348                 }\r
349 \r
350               }\r
351               j++;\r
352             }\r
353             nwidth += swidth;\r
354           }\r
355           // advance to begining of visible region\r
356           start = contigs[contig + 1] + contigs[contig + 2];\r
357           // add hidden segment to right of next region\r
358           for (int s = 0; s < sequences.length; s++)\r
359           {\r
360             SequenceI hseq = sequences[s].getSeq(gapCharacter)\r
361                     .getSubSequence(contigs[contig + 1], start);\r
362             if (alignment[s] == null)\r
363             {\r
364               alignment[s] = hseq;\r
365             }\r
366             else\r
367             {\r
368               alignment[s].setSequence(alignment[s].getSequenceAsString()\r
369                       + hseq.getSequenceAsString());\r
370               if (hseq.getEnd() >= hseq.getStart())\r
371               {\r
372                 alignment[s].setEnd(hseq.getEnd());\r
373               }\r
374             }\r
375           }\r
376           // mark hidden segment as hidden in the new alignment\r
377           columnselection.hideColumns(nwidth, nwidth + contigs[contig + 2]\r
378                   - 1);\r
379           nwidth += contigs[contig + 2];\r
380         }\r
381         // Do final segment - if it exists\r
382         if (j < nvismsa.length)\r
383         {\r
384           int swidth = 0;\r
385           if (nvismsa[j] != null)\r
386           {\r
387             SequenceI mseq[] = nvismsa[j];\r
388             AlignmentOrder order = (orders != null) ? orders[j] : null;\r
389             swidth = mseq[0].getLength();\r
390             for (int s = 0; s < mseq.length; s++)\r
391             {\r
392               if (alignment[s] == null)\r
393               {\r
394                 alignment[s] = mseq[s];\r
395               }\r
396               else\r
397               {\r
398                 alignment[s].setSequence(alignment[s].getSequenceAsString()\r
399                         + mseq[s].getSequenceAsString());\r
400                 if (mseq[s].getEnd() >= mseq[s].getStart())\r
401                 {\r
402                   alignment[s].setEnd(mseq[s].getEnd());\r
403                 }\r
404                 if (order != null)\r
405                 {\r
406                   order.updateSequence(mseq[s], alignment[s]);\r
407                 }\r
408               }\r
409             }\r
410           }\r
411           else\r
412           {\r
413             if (start < owidth)\r
414             {\r
415               // recover input data or place gaps\r
416               if (true)\r
417               {\r
418                 // recover input data\r
419                 for (int s = 0; s < sequences.length; s++)\r
420                 {\r
421                   SequenceI oseq = sequences[s].getSeq(gapCharacter)\r
422                           .getSubSequence(start, owidth + 1);\r
423                   if (swidth < oseq.getLength())\r
424                   {\r
425                     swidth = oseq.getLength();\r
426                   }\r
427                   if (alignment[s] == null)\r
428                   {\r
429                     alignment[s] = oseq;\r
430                   }\r
431                   else\r
432                   {\r
433                     alignment[s].setSequence(alignment[s]\r
434                             .getSequenceAsString()\r
435                             + oseq.getSequenceAsString());\r
436                     if (oseq.getEnd() >= oseq.getStart())\r
437                     {\r
438                       alignment[s].setEnd(oseq.getEnd());\r
439                     }\r
440                   }\r
441                 }\r
442                 nwidth += swidth;\r
443               }\r
444               else\r
445               {\r
446                 // place gaps.\r
447                 throw new Error("Padding not yet implemented.");\r
448               }\r
449             }\r
450           }\r
451         }\r
452       }\r
453       return new Object[]\r
454       { alignment, columnselection };\r
455     }\r
456     else\r
457     {\r
458       if (nvismsa.length != 1)\r
459       {\r
460         throw new Error(\r
461                 "Mismatch between visible blocks to update and number of contigs in view (contigs=0,blocks="\r
462                         + nvismsa.length);\r
463       }\r
464       if (nvismsa[0] != null)\r
465       {\r
466         return new Object[]\r
467         { nvismsa[0], new ColumnSelection() };\r
468       }\r
469       else\r
470       {\r
471         return getAlignmentAndColumnSelection(gapCharacter);\r
472       }\r
473     }\r
474   }\r
475 \r
476   /**\r
477    * returns simple array of start end positions of visible range on alignment.\r
478    * vis_start and vis_end are inclusive - use\r
479    * SequenceI.getSubSequence(vis_start, vis_end+1) to recover visible sequence\r
480    * from underlying alignment.\r
481    * \r
482    * @return int[] { start_i, end_i } for 1<i<n visible regions.\r
483    */\r
484   public int[] getVisibleContigs()\r
485   {\r
486     if (contigs != null && contigs.length > 0)\r
487     {\r
488       int start = 0;\r
489       int nvis = 0;\r
490       int fwidth = width;\r
491       for (int contig = 0; contig < contigs.length; contig += 3)\r
492       {\r
493         if ((contigs[contig + 1] - start) > 0)\r
494         {\r
495           nvis++;\r
496         }\r
497         fwidth += contigs[contig + 2]; // end up with full region width\r
498                                         // (including hidden regions)\r
499         start = contigs[contig + 1] + contigs[contig + 2];\r
500       }\r
501       if (start < fwidth)\r
502       {\r
503         nvis++;\r
504       }\r
505       int viscontigs[] = new int[nvis * 2];\r
506       nvis = 0;\r
507       start = 0;\r
508       for (int contig = 0; contig < contigs.length; contig += 3)\r
509       {\r
510         if ((contigs[contig + 1] - start) > 0)\r
511         {\r
512           viscontigs[nvis] = start;\r
513           viscontigs[nvis + 1] = contigs[contig + 1] - 1; // end is inclusive\r
514           nvis += 2;\r
515         }\r
516         start = contigs[contig + 1] + contigs[contig + 2];\r
517       }\r
518       if (start < fwidth)\r
519       {\r
520         viscontigs[nvis] = start;\r
521         viscontigs[nvis + 1] = fwidth; // end is inclusive\r
522         nvis += 2;\r
523       }\r
524       return viscontigs;\r
525     }\r
526     else\r
527     {\r
528       return new int[]\r
529       { 0, width };\r
530     }\r
531   }\r
532 \r
533   /**\r
534    * \r
535    * @return position of first visible column of AlignmentView within its\r
536    *         parent's alignment reference frame\r
537    */\r
538   public int getAlignmentOrigin()\r
539   {\r
540     // TODO Auto-generated method stub\r
541     return firstCol;\r
542   }\r
543 }\r