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