getSequenceStrings() bug fixed.
[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   public AlignmentView(CigarArray seqcigararray)
44   {
45     if (!seqcigararray.isSeqCigarArray())
46       throw new Error("Implementation Error - can only make an alignment view from a CigarArray of sequences.");
47     //contigs = seqcigararray.applyDeletions();
48     contigs = seqcigararray.getDeletedRegions();
49     sequences = seqcigararray.getSeqCigarArray();
50     width = seqcigararray.getWidth(); // visible width
51   }
52
53   public void setSequences(SeqCigar[] sequences)
54   {
55     this.sequences = sequences;
56   }
57
58   public void setContigs(int[] contigs)
59   {
60     this.contigs = contigs;
61   }
62
63   public SeqCigar[] getSequences()
64   {
65     return sequences;
66   }
67
68   public int[] getContigs()
69   {
70     return contigs;
71   }
72   /**
73    * get the full alignment and a columnselection object marking the hidden regions
74    * @param gapCharacter char
75    * @return Object[] { SequenceI[], ColumnSelection}
76    */
77   public Object[] getAlignmentAndColumnSelection(char gapCharacter) {
78     ColumnSelection colsel = new ColumnSelection();
79
80     return new Object[] { SeqCigar.createAlignmentSequences(sequences, gapCharacter, colsel, contigs), colsel};
81   }
82   /**
83    * getSequenceStrings
84    *
85    * @param c char
86    * @return String[]
87    */
88   public String[] getSequenceStrings(char c)
89   {
90     String[] seqs=new String[sequences.length];
91     for (int n=0; n<sequences.length; n++) {
92       String fullseq = sequences[n].getSequenceString(c);
93       if (contigs != null)
94       {
95         seqs[n] = "";
96         int p = 0;
97         for (int h = 0; h < contigs.length; h += 3)
98         {
99           seqs[n] += fullseq.substring(p, contigs[h + 1]);
100           p = contigs[h + 1] + contigs[h + 2];
101         }
102         seqs[n] += fullseq.substring(p);
103       } else
104         seqs[n] = fullseq;
105     }
106     return seqs;
107   }
108   /**
109    *
110    * @return visible number of columns in alignment view
111    */
112   public int getWidth() {
113     return width;
114   }
115
116   protected void setWidth(int width) {
117     this.width = width;
118   }
119   /**
120    * get the contiguous subalignments in an alignment view.
121    * @param gapCharacter char
122    * @return SequenceI[][]
123    */
124   public SequenceI[][] getVisibleContigs(char gapCharacter) {
125     SequenceI[][] smsa;
126     int njobs = 1;
127     if (sequences==null || width<=0)
128       return null;
129     if (contigs != null && contigs.length > 0)
130     {
131       int start = 0;
132       njobs = 0;
133       int fwidth = width;
134       for (int contig = 0; contig < contigs.length; contig += 3)
135       {
136         if ( (contigs[contig + 1] - start) > 0)
137         {
138           njobs++;
139         }
140         fwidth += contigs[contig + 2]; // end up with full region width (including hidden regions)
141         start = contigs[contig + 1] + contigs[contig + 2];
142       }
143       if (start < fwidth)
144       {
145         njobs++;
146       }
147       smsa = new SequenceI[njobs][];
148       start = 0;
149       int j = 0;
150       for (int contig = 0; contig < contigs.length; contig += 3)
151       {
152         if (contigs[contig + 1] - start > 0)
153         {
154           SequenceI mseq[] = new SequenceI[sequences.length];
155           for (int s = 0; s < mseq.length; s++)
156           {
157             mseq[s] = sequences[s].getSeq(gapCharacter).getSubSequence(start,
158                 contigs[contig + 1]);
159           }
160           smsa[j] = mseq;
161           j++;
162         }
163         start = contigs[contig + 1] + contigs[contig + 2];
164       }
165       if (start < fwidth)
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               fwidth + 1);
172         }
173         smsa[j] = mseq;
174         j++;
175       }
176     }
177     else
178     {
179       smsa = new SequenceI[1][];
180       smsa[0] = new SequenceI[sequences.length];
181       for (int s = 0; s < sequences.length; s++)
182       {
183         smsa[0][s] = sequences[s].getSeq(gapCharacter);
184       }
185     }
186     return smsa;
187   }
188   /**
189    * return full msa and hidden regions with visible blocks replaced with new sub alignments
190    * @param nvismsa SequenceI[][]
191    * @param orders AlignmentOrder[] corresponding to each SequenceI[] block.
192    * @return Object[]
193    */
194   public Object[] getUpdatedView(SequenceI[][] nvismsa, AlignmentOrder[] orders, char gapCharacter) {
195     if (sequences == null || width <= 0)
196     {
197       throw new Error("empty view cannot be updated.");
198     }
199     if (nvismsa == null)
200       throw new Error(
201           "nvismsa==null. use getAlignmentAndColumnSelection() instead.");
202     if (contigs != null && contigs.length > 0)
203     {
204       SequenceI[] alignment = new SequenceI[sequences.length];
205       ColumnSelection columnselection = new ColumnSelection();
206       if (contigs != null && contigs.length > 0)
207       {
208         int start = 0;
209         int nwidth = 0;
210         int owidth = width;
211         int j = 0;
212         for (int contig = 0; contig < contigs.length; contig += 3)
213         {
214           owidth += contigs[contig + 2]; // recover final column width
215           if (contigs[contig + 1] - start > 0)
216           {
217             int swidth = 0; // subalignment width
218             if (nvismsa[j] != null)
219             {
220               SequenceI mseq[] = nvismsa[j];
221               AlignmentOrder order=(orders==null) ? null : orders[j];
222               j++;
223               if (mseq.length!=sequences.length)
224                 throw new Error("Mismatch between number of sequences in block "+j+" ("+mseq.length+") and the original view ("+sequences.length+")");
225               swidth = mseq[0].getLength(); // JBPNote: could ensure padded here.
226               for (int s = 0; s < mseq.length; s++)
227               {
228                 if (alignment[s] == null)
229                 {
230                   alignment[s] = mseq[s];
231                 }
232                 else
233                 {
234                   alignment[s].setSequence(alignment[s].getSequence() +
235                                            mseq[s].getSequence());
236                   if (mseq[s].getStart() <= mseq[s].getEnd())
237                   {
238                     alignment[s].setEnd(mseq[s].getEnd());
239                   }
240                   if (order!=null) {
241                     order.updateSequence(mseq[s], alignment[s]);
242                   }
243                 }
244               }
245             }
246             else
247             {
248               // recover original alignment block or place gaps
249               if (true)
250               {
251                 // recover input data
252                 for (int s = 0; s < sequences.length; s++)
253                 {
254                   SequenceI oseq = sequences[s].getSeq(gapCharacter).getSubSequence(start,
255                       contigs[contig + 1]);
256                   if (swidth < oseq.getLength())
257                   {
258                     swidth = oseq.getLength();
259                   }
260                   if (alignment[s] == null)
261                   {
262                     alignment[s] = oseq;
263                   }
264                   else
265                   {
266                     alignment[s].setSequence(alignment[s].getSequence() +
267                                              oseq.getSequence());
268                     if (oseq.getEnd() >= oseq.getStart())
269                     {
270                       alignment[s].setEnd(oseq.getEnd());
271                     }
272                   }
273                 }
274
275               }
276               j++;
277             }
278             nwidth += swidth;
279           }
280           // advance to begining of visible region
281           start = contigs[contig + 1] + contigs[contig + 2];
282           // add hidden segment to right of next region
283           for (int s = 0; s < sequences.length; s++)
284           {
285             SequenceI hseq = sequences[s].getSeq(gapCharacter).getSubSequence(contigs[contig +
286                 1], start);
287             if (alignment[s] == null)
288             {
289               alignment[s] = hseq;
290             }
291             else
292             {
293               alignment[s].setSequence(alignment[s].getSequence() +
294                                        hseq.getSequence());
295               if (hseq.getEnd() >= hseq.getStart())
296               {
297                 alignment[s].setEnd(hseq.getEnd());
298               }
299             }
300           }
301           // mark hidden segment as hidden in the new alignment
302           columnselection.hideColumns(nwidth, nwidth + contigs[contig + 2] - 1);
303           nwidth += contigs[contig + 2];
304         }
305         // Do final segment - if it exists
306         if (j < nvismsa.length)
307         {
308           int swidth = 0;
309           if (nvismsa[j] != null)
310           {
311             SequenceI mseq[] = nvismsa[j];
312             AlignmentOrder order = (orders!=null) ? orders[j] : null;
313             swidth = mseq[0].getLength();
314             for (int s = 0; s < mseq.length; s++)
315             {
316               if (alignment[s] == null)
317               {
318                 alignment[s] = mseq[s];
319               }
320               else
321               {
322                 alignment[s].setSequence(alignment[s].getSequence() +
323                                          mseq[s].getSequence());
324                 if (mseq[s].getEnd() >= mseq[s].getStart())
325                 {
326                   alignment[s].setEnd(mseq[s].getEnd());
327                 }
328                 if (order!=null) {
329                   order.updateSequence(mseq[s], alignment[s]);
330                 }
331               }
332             }
333           }
334           else
335           {
336             if (start < owidth)
337             {
338               // recover input data or place gaps
339               if (true)
340               {
341                 // recover input data
342                 for (int s = 0; s < sequences.length; s++)
343                 {
344                   SequenceI oseq = sequences[s].getSeq(gapCharacter).getSubSequence(start,
345                       owidth + 1);
346                   if (swidth < oseq.getLength())
347                   {
348                     swidth = oseq.getLength();
349                   }
350                   if (alignment[s] == null)
351                   {
352                     alignment[s] = oseq;
353                   }
354                   else
355                   {
356                     alignment[s].setSequence(alignment[s].getSequence() +
357                                              oseq.getSequence());
358                     if (oseq.getEnd() >= oseq.getStart())
359                     {
360                       alignment[s].setEnd(oseq.getEnd());
361                     }
362                   }
363                 }
364                 nwidth += swidth;
365               }
366               else
367               {
368                 // place gaps.
369                 throw new Error("Padding not yet implemented.");
370               }
371             }
372           }
373         }
374       }
375       return new Object[] { alignment, columnselection};
376     } else {
377       if (nvismsa.length!=1)
378         throw new Error("Mismatch between visible blocks to update and number of contigs in view (contigs=0,blocks="+nvismsa.length);
379       if (nvismsa[0]!=null)
380         return new Object[] { nvismsa[0], new ColumnSelection()};
381       else
382         return getAlignmentAndColumnSelection(gapCharacter);
383     }
384   }
385
386 }