Merge branch 'develop' into features/JAL-845splitPaneMergeDevelop
[jalview.git] / src / jalview / datamodel / AlignedCodonFrame.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ 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 /**
26  * Stores mapping between the columns of a protein alignment and a DNA alignment
27  * and a list of individual codon to amino acid mappings between sequences.
28  */
29 public class AlignedCodonFrame
30 {
31
32   /*
33    * tied array of na Sequence objects.
34    */
35   private SequenceI[] dnaSeqs = null;
36
37   /*
38    * tied array of Mappings to protein sequence Objects and SequenceI[]
39    * aaSeqs=null; MapLists where each maps from the corresponding dnaSeqs
40    * element to corresponding aaSeqs element
41    */
42   private Mapping[] dnaToProt = null;
43
44   /**
45    * Constructor
46    */
47   public AlignedCodonFrame()
48   {
49   }
50
51   /**
52    * Adds a mapping between the dataset sequences for the associated dna and
53    * protein sequence objects
54    * 
55    * @param dnaseq
56    * @param aaseq
57    * @param map
58    */
59   public void addMap(SequenceI dnaseq, SequenceI aaseq, MapList map)
60   {
61     int nlen = 1;
62     if (dnaSeqs != null)
63     {
64       nlen = dnaSeqs.length + 1;
65     }
66     SequenceI[] ndna = new SequenceI[nlen];
67     Mapping[] ndtp = new Mapping[nlen];
68     if (dnaSeqs != null)
69     {
70       System.arraycopy(dnaSeqs, 0, ndna, 0, dnaSeqs.length);
71       System.arraycopy(dnaToProt, 0, ndtp, 0, dnaSeqs.length);
72     }
73     dnaSeqs = ndna;
74     dnaToProt = ndtp;
75     nlen--;
76     dnaSeqs[nlen] = (dnaseq.getDatasetSequence() == null) ? dnaseq : dnaseq
77             .getDatasetSequence();
78     Mapping mp = new Mapping(map);
79     // JBPNote DEBUG! THIS !
80     // dnaseq.transferAnnotation(aaseq, mp);
81     // aaseq.transferAnnotation(dnaseq, new Mapping(map.getInverse()));
82     mp.to = (aaseq.getDatasetSequence() == null) ? aaseq : aaseq
83             .getDatasetSequence();
84     dnaToProt[nlen] = mp;
85   }
86
87   public SequenceI[] getdnaSeqs()
88   {
89     return dnaSeqs;
90   }
91
92   public SequenceI[] getAaSeqs()
93   {
94     if (dnaToProt == null)
95     {
96       return null;
97     }
98     SequenceI[] sqs = new SequenceI[dnaToProt.length];
99     for (int sz = 0; sz < dnaToProt.length; sz++)
100     {
101       sqs[sz] = dnaToProt[sz].to;
102     }
103     return sqs;
104   }
105
106   public MapList[] getdnaToProt()
107   {
108     if (dnaToProt == null)
109     {
110       return null;
111     }
112     MapList[] sqs = new MapList[dnaToProt.length];
113     for (int sz = 0; sz < dnaToProt.length; sz++)
114     {
115       sqs[sz] = dnaToProt[sz].map;
116     }
117     return sqs;
118   }
119
120   public Mapping[] getProtMappings()
121   {
122     return dnaToProt;
123   }
124
125   /**
126    * Returns the first mapping found which is to or from the given sequence, or
127    * null.
128    * 
129    * @param seq
130    * @return
131    */
132   public Mapping getMappingForSequence(SequenceI seq)
133   {
134     if (dnaSeqs == null)
135     {
136       return null;
137     }
138     SequenceI seqDs = seq.getDatasetSequence();
139     seqDs = seqDs != null ? seqDs : seq;
140
141     for (int ds = 0; ds < dnaSeqs.length; ds++)
142     {
143       if (dnaSeqs[ds] == seqDs || dnaToProt[ds].to == seqDs)
144       {
145         return dnaToProt[ds];
146       }
147     }
148     return null;
149   }
150
151   /**
152    * Return the corresponding aligned or dataset aa sequence for given dna
153    * sequence, null if not found.
154    * 
155    * @param sequenceRef
156    * @return
157    */
158   public SequenceI getAaForDnaSeq(SequenceI dnaSeqRef)
159   {
160     if (dnaSeqs == null)
161     {
162       return null;
163     }
164     SequenceI dnads = dnaSeqRef.getDatasetSequence();
165     for (int ds = 0; ds < dnaSeqs.length; ds++)
166     {
167       if (dnaSeqs[ds] == dnaSeqRef || dnaSeqs[ds] == dnads)
168       {
169         return dnaToProt[ds].to;
170       }
171     }
172     return null;
173   }
174
175   /**
176    * 
177    * @param sequenceRef
178    * @return null or corresponding aaSeq entry for dnaSeq entry
179    */
180   public SequenceI getDnaForAaSeq(SequenceI aaSeqRef)
181   {
182     if (dnaToProt == null)
183     {
184       return null;
185     }
186     SequenceI aads = aaSeqRef.getDatasetSequence();
187     for (int as = 0; as < dnaToProt.length; as++)
188     {
189       if (dnaToProt[as].to == aaSeqRef || dnaToProt[as].to == aads)
190       {
191         return dnaSeqs[as];
192       }
193     }
194     return null;
195   }
196
197   /**
198    * test to see if codon frame involves seq in any way
199    * 
200    * @param seq
201    *          a nucleotide or protein sequence
202    * @return true if a mapping exists to or from this sequence to any translated
203    *         sequence
204    */
205   public boolean involvesSequence(SequenceI seq)
206   {
207     return getAaForDnaSeq(seq) != null || getDnaForAaSeq(seq) != null;
208   }
209
210   /**
211    * Add search results for regions in other sequences that translate or are
212    * translated from a particular position in seq
213    * 
214    * @param seq
215    * @param index
216    *          position in seq
217    * @param results
218    *          where highlighted regions go
219    */
220   public void markMappedRegion(SequenceI seq, int index,
221           SearchResults results)
222   {
223     if (dnaToProt == null)
224     {
225       return;
226     }
227     int[] codon;
228     SequenceI ds = seq.getDatasetSequence();
229     for (int mi = 0; mi < dnaToProt.length; mi++)
230     {
231       if (dnaSeqs[mi] == seq || dnaSeqs[mi] == ds)
232       {
233         // DEBUG System.err.println("dna pos "+index);
234         codon = dnaToProt[mi].map.locateInTo(index, index);
235         if (codon != null)
236         {
237           for (int i = 0; i < codon.length; i += 2)
238           {
239             results.addResult(dnaToProt[mi].to, codon[i], codon[i + 1]);
240           }
241         }
242       }
243       else if (dnaToProt[mi].to == seq || dnaToProt[mi].to == ds)
244       {
245         // DEBUG System.err.println("aa pos "+index);
246         {
247           codon = dnaToProt[mi].map.locateInFrom(index, index);
248           if (codon != null)
249           {
250             for (int i = 0; i < codon.length; i += 2)
251             {
252               results.addResult(dnaSeqs[mi], codon[i], codon[i + 1]);
253             }
254           }
255         }
256       }
257     }
258   }
259
260   /**
261    * Returns the DNA codon positions (base 1) for the given position (base 1) in
262    * a mapped protein sequence, or null if no mapping is found.
263    * 
264    * Intended for use in aligning cDNA to match aligned protein. Only the first
265    * mapping found is returned, so not suitable for use if multiple protein
266    * sequences are mapped to the same cDNA (but aligning cDNA as protein is
267    * ill-defined for this case anyway).
268    * 
269    * @param seq
270    *          the DNA dataset sequence
271    * @param aaPos
272    *          residue position (base 1) in a protein sequence
273    * @return
274    */
275   public int[] getDnaPosition(SequenceI seq, int aaPos)
276   {
277     /*
278      * Adapted from markMappedRegion().
279      */
280     MapList ml = null;
281     for (int i = 0; i < dnaToProt.length; i++)
282     {
283       if (dnaSeqs[i] == seq)
284       {
285         ml = getdnaToProt()[i];
286         break;
287       }
288     }
289     return ml == null ? null : ml.locateInFrom(aaPos, aaPos);
290   }
291
292   /**
293    * Convenience method to return the first aligned sequence in the given
294    * alignment whose dataset has a mapping with the given dataset sequence.
295    * 
296    * @param seq
297    * 
298    * @param al
299    * @return
300    */
301   public SequenceI findAlignedSequence(SequenceI seq, AlignmentI al)
302   {
303     /*
304      * Search mapped protein ('to') sequences first.
305      */
306     if (this.dnaToProt != null)
307     {
308       for (int i = 0; i < dnaToProt.length; i++)
309       {
310         if (this.dnaSeqs[i] == seq)
311         {
312           for (SequenceI sourceAligned : al.getSequences())
313           {
314             if (this.dnaToProt[i].to == sourceAligned.getDatasetSequence())
315             {
316               return sourceAligned;
317             }
318           }
319         }
320       }
321     }
322
323     /*
324      * Then try mapped dna sequences.
325      */
326     if (this.dnaToProt != null)
327     {
328       for (int i = 0; i < dnaToProt.length; i++)
329       {
330         if (this.dnaToProt[i].to == seq)
331         {
332           for (SequenceI sourceAligned : al.getSequences())
333           {
334             if (this.dnaSeqs[i] == sourceAligned.getDatasetSequence())
335             {
336               return sourceAligned;
337             }
338           }
339         }
340       }
341     }
342
343     return null;
344   }
345
346   /**
347    * Returns the region in the 'mappedFrom' sequence's dataset that is mapped to
348    * position 'pos' (base 1) in the 'mappedTo' sequence's dataset. The region is
349    * a set of start/end position pairs.
350    * 
351    * @param mappedFrom
352    * @param mappedTo
353    * @param pos
354    * @return
355    */
356   public int[] getMappedRegion(SequenceI mappedFrom, SequenceI mappedTo,
357           int pos)
358   {
359     SequenceI targetDs = mappedFrom.getDatasetSequence() == null ? mappedFrom
360             : mappedFrom.getDatasetSequence();
361     SequenceI sourceDs = mappedTo.getDatasetSequence() == null ? mappedTo
362             : mappedTo.getDatasetSequence();
363     if (targetDs == null || sourceDs == null || dnaToProt == null)
364     {
365       return null;
366     }
367     for (int mi = 0; mi < dnaToProt.length; mi++)
368     {
369       if (dnaSeqs[mi] == targetDs && dnaToProt[mi].to == sourceDs)
370       {
371         int[] codon = dnaToProt[mi].map.locateInFrom(pos, pos);
372         if (codon != null) {
373           return codon;
374         }
375       }
376     }
377     return null;
378   }
379 }