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