4016ee58ff29408626a5881ab01cf376583d7c7c
[jalview.git] / src / jalview / datamodel / AlignedCodonFrame.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
3  * Copyright (C) 2014 The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.datamodel;
22
23 import jalview.util.MapList;
24
25 import java.util.Enumeration;
26 import java.util.Vector;
27
28 /**
29  * Stores mapping between the columns of a protein alignment and a DNA alignment
30  * and a list of individual codon to amino acid mappings between sequences.
31  */
32
33 public class AlignedCodonFrame
34 {
35   /**
36    * array of nucleotide positions for aligned codons at column of aligned
37    * proteins.
38    */
39   public int[][] codons = null;
40
41   /**
42    * width of protein sequence alignement implicit assertion that codons.length
43    * >= aaWidth
44    */
45   public int aaWidth = 0;
46
47   /**
48    * initialise codon frame with a nominal alignment width
49    * 
50    * @param aWidth
51    */
52   public AlignedCodonFrame(int aWidth)
53   {
54     if (aWidth <= 0)
55     {
56       codons = null;
57       return;
58     }
59     codons = new int[aWidth][];
60     for (int res = 0; res < aWidth; res++)
61     {
62       codons[res] = null;
63     }
64   }
65
66   /**
67    * ensure that codons array is at least as wide as aslen residues
68    * 
69    * @param aslen
70    * @return (possibly newly expanded) codon array
71    */
72   public int[][] checkCodonFrameWidth(int aslen)
73   {
74     if (codons.length <= aslen + 1)
75     {
76       // probably never have to do this ?
77       int[][] c = new int[codons.length + 10][];
78       for (int i = 0; i < codons.length; i++)
79       {
80         c[i] = codons[i];
81         codons[i] = null;
82       }
83       codons = c;
84     }
85     return codons;
86   }
87
88   /**
89    * @return width of aligned translated amino acid residues
90    */
91   public int getaaWidth()
92   {
93     return aaWidth;
94   }
95
96   /**
97    * TODO: not an ideal solution - we reference the aligned amino acid sequences
98    * in order to make insertions on them Better would be dnaAlignment and
99    * aaAlignment reference....
100    */
101   Vector a_aaSeqs = new Vector();
102
103   /**
104    * increase aaWidth by one and insert a new aligned codon position space at
105    * aspos.
106    * 
107    * @param aspos
108    */
109   public void insertAAGap(int aspos, char gapCharacter)
110   {
111     // this aa appears before the aligned codons at aspos - so shift them in
112     // each pair of mapped sequences
113     aaWidth++;
114     if (a_aaSeqs != null)
115     {
116       // we actually have to modify the aligned sequences here, so use the
117       // a_aaSeqs vector
118       Enumeration sq = a_aaSeqs.elements();
119       while (sq.hasMoreElements())
120       {
121         ((SequenceI) sq.nextElement()).insertCharAt(aspos, gapCharacter);
122       }
123     }
124     checkCodonFrameWidth(aspos);
125     if (aspos < aaWidth)
126     {
127       aaWidth++;
128       System.arraycopy(codons, aspos, codons, aspos + 1, codons.length
129               - aspos - 1);
130       codons[aspos] = null; // clear so new codon position can be marked.
131     }
132   }
133
134   public void setAaWidth(int aapos)
135   {
136     aaWidth = aapos;
137   }
138
139   /**
140    * tied array of na Sequence objects.
141    */
142   SequenceI[] dnaSeqs = null;
143
144   /**
145    * tied array of Mappings to protein sequence Objects and SequenceI[]
146    * aaSeqs=null; MapLists where eac maps from the corresponding dnaSeqs element
147    * to corresponding aaSeqs element
148    */
149   Mapping[] dnaToProt = null;
150
151   /**
152    * add a mapping between the dataset sequences for the associated dna and
153    * protein sequence objects
154    * 
155    * @param dnaseq
156    * @param aaseq
157    * @param map
158    */
159   public void addMap(SequenceI dnaseq, SequenceI aaseq, MapList map)
160   {
161     int nlen = 1;
162     if (dnaSeqs != null)
163     {
164       nlen = dnaSeqs.length + 1;
165     }
166     SequenceI[] ndna = new SequenceI[nlen];
167     Mapping[] ndtp = new Mapping[nlen];
168     if (dnaSeqs != null)
169     {
170       System.arraycopy(dnaSeqs, 0, ndna, 0, dnaSeqs.length);
171       System.arraycopy(dnaToProt, 0, ndtp, 0, dnaSeqs.length);
172     }
173     dnaSeqs = ndna;
174     dnaToProt = ndtp;
175     nlen--;
176     dnaSeqs[nlen] = (dnaseq.getDatasetSequence() == null) ? dnaseq : dnaseq
177             .getDatasetSequence();
178     Mapping mp = new Mapping(map);
179     // JBPNote DEBUG! THIS !
180     // dnaseq.transferAnnotation(aaseq, mp);
181     // aaseq.transferAnnotation(dnaseq, new Mapping(map.getInverse()));
182     mp.to = (aaseq.getDatasetSequence() == null) ? aaseq : aaseq
183             .getDatasetSequence();
184     a_aaSeqs.addElement(aaseq);
185     dnaToProt[nlen] = mp;
186   }
187
188   public SequenceI[] getdnaSeqs()
189   {
190     return dnaSeqs;
191   }
192
193   public SequenceI[] getAaSeqs()
194   {
195     if (dnaToProt == null)
196     {
197       return null;
198     }
199     SequenceI[] sqs = new SequenceI[dnaToProt.length];
200     for (int sz = 0; sz < dnaToProt.length; sz++)
201     {
202       sqs[sz] = dnaToProt[sz].to;
203     }
204     return sqs;
205   }
206
207   public MapList[] getdnaToProt()
208   {
209     if (dnaToProt == null)
210     {
211       return null;
212     }
213     MapList[] sqs = new MapList[dnaToProt.length];
214     for (int sz = 0; sz < dnaToProt.length; sz++)
215     {
216       sqs[sz] = dnaToProt[sz].map;
217     }
218     return sqs;
219   }
220
221   public Mapping[] getProtMappings()
222   {
223     return dnaToProt;
224   }
225
226   /**
227    * 
228    * @param sequenceRef
229    * @return null or corresponding aaSeq dataset sequence for dnaSeq entry
230    */
231   public SequenceI getAaForDnaSeq(SequenceI dnaSeqRef)
232   {
233     return getAaForDnaSeq(dnaSeqRef, true);
234   }
235
236   /**
237    * Return the corresponding aligned or dataset aa sequence for given dna
238    * sequence, null if not found.
239    * 
240    * @param sequenceRef
241    * @param returnDataset
242    *          if true, return the aa dataset, else the aligned sequence
243    * @return
244    */
245   public SequenceI getAaForDnaSeq(SequenceI dnaSeqRef, boolean returnDataset)
246   {
247     if (dnaSeqs == null)
248     {
249       return null;
250     }
251     SequenceI dnads = dnaSeqRef.getDatasetSequence();
252     for (int ds = 0; ds < dnaSeqs.length; ds++)
253     {
254       if (dnaSeqs[ds] == dnaSeqRef || dnaSeqs[ds] == dnads)
255       {
256         if (returnDataset)
257         {
258           return dnaToProt[ds].to;
259         }
260         else
261         {
262           // TODO very fragile - depends on dnaSeqs, dnaToProt, a_aaSeqs moving
263           // in parallel; revise data model to guarantee this
264           return (SequenceI) a_aaSeqs.elementAt(ds);
265         }
266       }
267     }
268     return null;
269   }
270
271   /**
272    * 
273    * @param sequenceRef
274    * @return null or corresponding aaSeq entry for dnaSeq entry
275    */
276   public SequenceI getDnaForAaSeq(SequenceI aaSeqRef)
277   {
278     if (dnaToProt == null)
279     {
280       return null;
281     }
282     SequenceI aads = aaSeqRef.getDatasetSequence();
283     for (int as = 0; as < dnaToProt.length; as++)
284     {
285       if (dnaToProt[as].to == aaSeqRef || dnaToProt[as].to == aads)
286       {
287         return dnaSeqs[as];
288       }
289     }
290     return null;
291   }
292
293   /**
294    * test to see if codon frame involves seq in any way
295    * 
296    * @param seq
297    *          a nucleotide or protein sequence
298    * @return true if a mapping exists to or from this sequence to any translated
299    *         sequence
300    */
301   public boolean involvesSequence(SequenceI seq)
302   {
303     return getAaForDnaSeq(seq) != null || getDnaForAaSeq(seq) != null;
304   }
305
306   /**
307    * Add search results for regions in other sequences that translate or are
308    * translated from a particular position in seq
309    * 
310    * @param seq
311    * @param index
312    *          position in seq
313    * @param results
314    *          where highlighted regions go
315    */
316   public void markMappedRegion(SequenceI seq, int index,
317           SearchResults results)
318   {
319     if (dnaToProt == null)
320     {
321       return;
322     }
323     int[] codon;
324     SequenceI ds = seq.getDatasetSequence();
325     for (int mi = 0; mi < dnaToProt.length; mi++)
326     {
327       if (dnaSeqs[mi] == seq || dnaSeqs[mi] == ds)
328       {
329         // DEBUG System.err.println("dna pos "+index);
330         codon = dnaToProt[mi].map.locateInTo(index, index);
331         if (codon != null)
332         {
333           for (int i = 0; i < codon.length; i += 2)
334           {
335             results.addResult(dnaToProt[mi].to, codon[i], codon[i + 1]);
336           }
337         }
338       }
339       else if (dnaToProt[mi].to == seq || dnaToProt[mi].to == ds)
340       {
341         // DEBUG System.err.println("aa pos "+index);
342         {
343           codon = dnaToProt[mi].map.locateInFrom(index, index);
344           if (codon != null)
345           {
346             for (int i = 0; i < codon.length; i += 2)
347             {
348               results.addResult(dnaSeqs[mi], codon[i], codon[i + 1]);
349             }
350           }
351         }
352       }
353     }
354   }
355
356   /**
357    * Returns the DNA codon positions (base 1) for the given position (base 1) in
358    * a mapped protein sequence, or null if no mapping is found.
359    * 
360    * Intended for use in aligning cDNA to match aligned protein. Only the first
361    * mapping found is returned, so not suitable for use if multiple protein
362    * sequences are mapped to the same cDNA (but aligning cDNA as protein is
363    * ill-defined for this case anyway).
364    * 
365    * @param seq
366    *          the DNA dataset sequence
367    * @param aaPos
368    *          residue position (base 1) in a protein sequence
369    * @return
370    */
371   public int[] getDnaPosition(SequenceI seq, int aaPos)
372   {
373     /*
374      * Adapted from markMappedRegion().
375      */
376     MapList ml = null;
377     for (int i = 0; i < dnaToProt.length; i++)
378     {
379       if (dnaSeqs[i] == seq)
380       {
381         ml = getdnaToProt()[i];
382         break;
383       }
384     }
385     return ml == null ? null : ml.locateInFrom(aaPos, aaPos);
386   }
387 }