JAL-1432 updated copyright notices
[jalview.git] / src / jalview / analysis / Dna.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.0b1)
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 of the License, or (at your option) any later version.
10  *  
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  * The Jalview Authors are detailed in the 'AUTHORS' file.
18  */
19 package jalview.analysis;
20
21 import java.util.ArrayList;
22 import java.util.Hashtable;
23 import java.util.Vector;
24
25 import jalview.datamodel.AlignedCodonFrame;
26 import jalview.datamodel.Alignment;
27 import jalview.datamodel.AlignmentAnnotation;
28 import jalview.datamodel.AlignmentI;
29 import jalview.datamodel.Annotation;
30 import jalview.datamodel.DBRefEntry;
31 import jalview.datamodel.FeatureProperties;
32 import jalview.datamodel.Mapping;
33 import jalview.datamodel.Sequence;
34 import jalview.datamodel.SequenceFeature;
35 import jalview.datamodel.SequenceI;
36 import jalview.schemes.ResidueProperties;
37 import jalview.util.MapList;
38 import jalview.util.ShiftList;
39
40 public class Dna
41 {
42   /**
43    * 
44    * @param cdp1
45    * @param cdp2
46    * @return -1 if cdp1 aligns before cdp2, 0 if in the same column or cdp2 is
47    *         null, +1 if after cdp2
48    */
49   private static int compare_codonpos(int[] cdp1, int[] cdp2)
50   {
51     if (cdp2 == null
52             || (cdp1[0] == cdp2[0] && cdp1[1] == cdp2[1] && cdp1[2] == cdp2[2]))
53       return 0;
54     if (cdp1[0] < cdp2[0] || cdp1[1] < cdp2[1] || cdp1[2] < cdp2[2])
55       return -1; // one base in cdp1 precedes the corresponding base in the
56     // other codon
57     return 1; // one base in cdp1 appears after the corresponding base in the
58     // other codon.
59   }
60
61   /**
62    * DNA->mapped protein sequence alignment translation given set of sequences
63    * 1. id distinct coding regions within selected region for each sequence 2.
64    * generate peptides based on inframe (or given) translation or (optionally
65    * and where specified) out of frame translations (annotated appropriately) 3.
66    * align peptides based on codon alignment
67    */
68   /**
69    * id potential products from dna 1. search for distinct products within
70    * selected region for each selected sequence 2. group by associated DB type.
71    * 3. return as form for input into above function
72    */
73   /**
74    * 
75    */
76   /**
77    * create a new alignment of protein sequences by an inframe translation of
78    * the provided NA sequences
79    * 
80    * @param selection
81    * @param seqstring
82    * @param viscontigs
83    * @param gapCharacter
84    * @param annotations
85    * @param aWidth
86    * @param dataset
87    *          destination dataset for translated sequences and mappings
88    * @return
89    */
90   public static AlignmentI CdnaTranslate(SequenceI[] selection,
91           String[] seqstring, int viscontigs[], char gapCharacter,
92           AlignmentAnnotation[] annotations, int aWidth, Alignment dataset)
93   {
94     return CdnaTranslate(selection, seqstring, null, viscontigs,
95             gapCharacter, annotations, aWidth, dataset);
96   }
97
98   /**
99    * 
100    * @param selection
101    * @param seqstring
102    * @param product
103    *          - array of DbRefEntry objects from which exon map in seqstring is
104    *          derived
105    * @param viscontigs
106    * @param gapCharacter
107    * @param annotations
108    * @param aWidth
109    * @param dataset
110    * @return
111    */
112   public static AlignmentI CdnaTranslate(SequenceI[] selection,
113           String[] seqstring, DBRefEntry[] product, int viscontigs[],
114           char gapCharacter, AlignmentAnnotation[] annotations, int aWidth,
115           Alignment dataset)
116   {
117     AlignedCodonFrame codons = new AlignedCodonFrame(aWidth); // stores hash of
118     // subsequent
119     // positions for
120     // each codon
121     // start position
122     // in alignment
123     int s, sSize = selection.length;
124     Vector pepseqs = new Vector();
125     for (s = 0; s < sSize; s++)
126     {
127       SequenceI newseq = translateCodingRegion(selection[s], seqstring[s],
128               viscontigs, codons, gapCharacter,
129               (product != null) ? product[s] : null, false); // possibly anonymous
130       // product
131       if (newseq != null)
132       {
133         pepseqs.addElement(newseq);
134         SequenceI ds = newseq;
135         if (dataset != null)
136         {
137           while (ds.getDatasetSequence() != null)
138           {
139             ds = ds.getDatasetSequence();
140           }
141           dataset.addSequence(ds);
142         }
143       }
144     }
145     if (codons.aaWidth == 0)
146       return null;
147     SequenceI[] newseqs = new SequenceI[pepseqs.size()];
148     pepseqs.copyInto(newseqs);
149     AlignmentI al = new Alignment(newseqs);
150     al.padGaps(); // ensure we look aligned.
151     al.setDataset(dataset);
152     translateAlignedAnnotations(annotations, al, codons);
153     al.addCodonFrame(codons);
154     return al;
155   }
156
157   /**
158    * fake the collection of DbRefs with associated exon mappings to identify if
159    * a translation would generate distinct product in the currently selected
160    * region.
161    * 
162    * @param selection
163    * @param viscontigs
164    * @return
165    */
166   public static boolean canTranslate(SequenceI[] selection,
167           int viscontigs[])
168   {
169     for (int gd = 0; gd < selection.length; gd++)
170     {
171       SequenceI dna = selection[gd];
172       jalview.datamodel.DBRefEntry[] dnarefs = jalview.util.DBRefUtils
173               .selectRefs(dna.getDBRef(),
174                       jalview.datamodel.DBRefSource.DNACODINGDBS);
175       if (dnarefs != null)
176       {
177         // intersect with pep
178         // intersect with pep
179         Vector mappedrefs = new Vector();
180         DBRefEntry[] refs = dna.getDBRef();
181         for (int d = 0; d < refs.length; d++)
182         {
183           if (refs[d].getMap() != null && refs[d].getMap().getMap() != null
184                   && refs[d].getMap().getMap().getFromRatio() == 3
185                   && refs[d].getMap().getMap().getToRatio() == 1)
186           {
187             mappedrefs.addElement(refs[d]); // add translated protein maps
188           }
189         }
190         dnarefs = new DBRefEntry[mappedrefs.size()];
191         mappedrefs.copyInto(dnarefs);
192         for (int d = 0; d < dnarefs.length; d++)
193         {
194           Mapping mp = dnarefs[d].getMap();
195           if (mp != null)
196           {
197             for (int vc = 0; vc < viscontigs.length; vc += 2)
198             {
199               int[] mpr = mp.locateMappedRange(viscontigs[vc],
200                       viscontigs[vc + 1]);
201               if (mpr != null)
202               {
203                 return true;
204               }
205             }
206           }
207         }
208       }
209     }
210     return false;
211   }
212
213   /**
214    * generate a set of translated protein products from annotated sequenceI
215    * 
216    * @param selection
217    * @param viscontigs
218    * @param gapCharacter
219    * @param dataset
220    *          destination dataset for translated sequences
221    * @param annotations
222    * @param aWidth
223    * @return
224    */
225   public static AlignmentI CdnaTranslate(SequenceI[] selection,
226           int viscontigs[], char gapCharacter, Alignment dataset)
227   {
228     int alwidth = 0;
229     Vector cdnasqs = new Vector();
230     Vector cdnasqi = new Vector();
231     Vector cdnaprod = new Vector();
232     for (int gd = 0; gd < selection.length; gd++)
233     {
234       SequenceI dna = selection[gd];
235       jalview.datamodel.DBRefEntry[] dnarefs = jalview.util.DBRefUtils
236               .selectRefs(dna.getDBRef(),
237                       jalview.datamodel.DBRefSource.DNACODINGDBS);
238       if (dnarefs != null)
239       {
240         // intersect with pep
241         Vector mappedrefs = new Vector();
242         DBRefEntry[] refs = dna.getDBRef();
243         for (int d = 0; d < refs.length; d++)
244         {
245           if (refs[d].getMap() != null && refs[d].getMap().getMap() != null
246                   && refs[d].getMap().getMap().getFromRatio() == 3
247                   && refs[d].getMap().getMap().getToRatio() == 1)
248           {
249             mappedrefs.addElement(refs[d]); // add translated protein maps
250           }
251         }
252         dnarefs = new DBRefEntry[mappedrefs.size()];
253         mappedrefs.copyInto(dnarefs);
254         for (int d = 0; d < dnarefs.length; d++)
255         {
256           Mapping mp = dnarefs[d].getMap();
257           StringBuffer sqstr = new StringBuffer();
258           if (mp != null)
259           {
260             Mapping intersect = mp.intersectVisContigs(viscontigs);
261             // generate seqstring for this sequence based on mapping
262
263             if (sqstr.length() > alwidth)
264               alwidth = sqstr.length();
265             cdnasqs.addElement(sqstr.toString());
266             cdnasqi.addElement(dna);
267             cdnaprod.addElement(intersect);
268           }
269         }
270       }
271       SequenceI[] cdna = new SequenceI[cdnasqs.size()];
272       DBRefEntry[] prods = new DBRefEntry[cdnaprod.size()];
273       String[] xons = new String[cdnasqs.size()];
274       cdnasqs.copyInto(xons);
275       cdnaprod.copyInto(prods);
276       cdnasqi.copyInto(cdna);
277       return CdnaTranslate(cdna, xons, prods, viscontigs, gapCharacter,
278               null, alwidth, dataset);
279     }
280     return null;
281   }
282
283   /**
284    * translate na alignment annotations onto translated amino acid alignment al
285    * using codon mapping codons
286    * 
287    * @param annotations
288    * @param al
289    * @param codons
290    */
291   public static void translateAlignedAnnotations(
292           AlignmentAnnotation[] annotations, AlignmentI al,
293           AlignedCodonFrame codons)
294   {
295     // //////////////////////////////
296     // Copy annotations across
297     //
298     // Can only do this for columns with consecutive codons, or where
299     // annotation is sequence associated.
300
301     int pos, a, aSize;
302     if (annotations != null)
303     {
304       for (int i = 0; i < annotations.length; i++)
305       {
306         // Skip any autogenerated annotation
307         if (annotations[i].autoCalculated)
308         {
309           continue;
310         }
311
312         aSize = codons.getaaWidth(); // aa alignment width.
313         jalview.datamodel.Annotation[] anots = (annotations[i].annotations == null) ? null
314                 : new jalview.datamodel.Annotation[aSize];
315         if (anots != null)
316         {
317           for (a = 0; a < aSize; a++)
318           {
319             // process through codon map.
320             if (codons.codons[a] != null
321                     && codons.codons[a][0] == (codons.codons[a][2] - 2))
322             {
323               anots[a] = getCodonAnnotation(codons.codons[a],
324                       annotations[i].annotations);
325             }
326           }
327         }
328
329         jalview.datamodel.AlignmentAnnotation aa = new jalview.datamodel.AlignmentAnnotation(
330                 annotations[i].label, annotations[i].description, anots);
331         aa.graph = annotations[i].graph;
332         aa.graphGroup = annotations[i].graphGroup;
333         aa.graphHeight = annotations[i].graphHeight;
334         if (annotations[i].getThreshold() != null)
335         {
336           aa.setThreshold(new jalview.datamodel.GraphLine(annotations[i]
337                   .getThreshold()));
338         }
339         if (annotations[i].hasScore)
340         {
341           aa.setScore(annotations[i].getScore());
342         }
343         if (annotations[i].sequenceRef != null)
344         {
345           SequenceI aaSeq = codons
346                   .getAaForDnaSeq(annotations[i].sequenceRef);
347           if (aaSeq != null)
348           {
349             // aa.compactAnnotationArray(); // throw away alignment annotation
350             // positioning
351             aa.setSequenceRef(aaSeq);
352             aa.createSequenceMapping(aaSeq, aaSeq.getStart(), true); // rebuild
353             // mapping
354             aa.adjustForAlignment();
355             aaSeq.addAlignmentAnnotation(aa);
356           }
357
358         }
359         al.addAnnotation(aa);
360       }
361     }
362   }
363
364   private static Annotation getCodonAnnotation(int[] is,
365           Annotation[] annotations)
366   {
367     // Have a look at all the codon positions for annotation and put the first
368     // one found into the translated annotation pos.
369     int contrib = 0;
370     Annotation annot = null;
371     for (int p = 0; p < 3; p++)
372     {
373       if (annotations[is[p]] != null)
374       {
375         if (annot == null)
376         {
377           annot = new Annotation(annotations[is[p]]);
378           contrib = 1;
379         }
380         else
381         {
382           // merge with last
383           Annotation cpy = new Annotation(annotations[is[p]]);
384           if (annot.colour == null)
385           {
386             annot.colour = cpy.colour;
387           }
388           if (annot.description == null || annot.description.length() == 0)
389           {
390             annot.description = cpy.description;
391           }
392           if (annot.displayCharacter == null)
393           {
394             annot.displayCharacter = cpy.displayCharacter;
395           }
396           if (annot.secondaryStructure == 0)
397           {
398             annot.secondaryStructure = cpy.secondaryStructure;
399           }
400           annot.value += cpy.value;
401           contrib++;
402         }
403       }
404     }
405     if (contrib > 1)
406     {
407       annot.value /= (float) contrib;
408     }
409     return annot;
410   }
411
412   /**
413    * Translate a na sequence
414    * 
415    * @param selection
416    *          sequence displayed under viscontigs visible columns
417    * @param seqstring
418    *          ORF read in some global alignment reference frame
419    * @param viscontigs
420    *          mapping from global reference frame to visible seqstring ORF read
421    * @param codons
422    *          Definition of global ORF alignment reference frame
423    * @param gapCharacter
424    * @return sequence ready to be added to alignment.
425    * @deprecated Use {@link #translateCodingRegion(SequenceI,String,int[],AlignedCodonFrame,char,DBRefEntry,boolean)} instead
426    */
427   public static SequenceI translateCodingRegion(SequenceI selection,
428           String seqstring, int[] viscontigs, AlignedCodonFrame codons,
429           char gapCharacter, DBRefEntry product)
430   {
431     return translateCodingRegion(selection, seqstring, viscontigs, codons,
432             gapCharacter, product, false);
433   }
434
435   /**
436    * Translate a na sequence
437    * 
438    * @param selection
439    *          sequence displayed under viscontigs visible columns
440    * @param seqstring
441    *          ORF read in some global alignment reference frame
442    * @param viscontigs
443    *          mapping from global reference frame to visible seqstring ORF read
444    * @param codons
445    *          Definition of global ORF alignment reference frame
446    * @param gapCharacter
447    * @param starForStop when true stop codons will translate as '*', otherwise as 'X'  
448    * @return sequence ready to be added to alignment.
449    */
450   public static SequenceI translateCodingRegion(SequenceI selection,
451           String seqstring, int[] viscontigs, AlignedCodonFrame codons,
452           char gapCharacter, DBRefEntry product, final boolean starForStop)
453   {
454     java.util.List skip = new ArrayList();
455     int skipint[] = null;
456     ShiftList vismapping = new ShiftList(); // map from viscontigs to seqstring
457     // intervals
458     int vc, scontigs[] = new int[viscontigs.length];
459     int npos = 0;
460     for (vc = 0; vc < viscontigs.length; vc += 2)
461     {
462       if (vc == 0)
463       {
464         vismapping.addShift(npos, viscontigs[vc]);
465       }
466       else
467       {
468         // hidden region
469         vismapping.addShift(npos, viscontigs[vc] - viscontigs[vc - 1] + 1);
470       }
471       scontigs[vc] = viscontigs[vc];
472       scontigs[vc + 1] = viscontigs[vc + 1];
473     }
474
475     StringBuffer protein = new StringBuffer();
476     String seq = seqstring.replace('U', 'T');
477     char codon[] = new char[3];
478     int cdp[] = new int[3], rf = 0, lastnpos = 0, nend;
479     int aspos = 0;
480     int resSize = 0;
481     for (npos = 0, nend = seq.length(); npos < nend; npos++)
482     {
483       if (!jalview.util.Comparison.isGap(seq.charAt(npos)))
484       {
485         cdp[rf] = npos; // store position
486         codon[rf++] = seq.charAt(npos); // store base
487       }
488       // filled an RF yet ?
489       if (rf == 3)
490       {
491         String aa = ResidueProperties.codonTranslate(new String(codon));
492         rf = 0;
493         if (aa == null)
494         {
495           aa = String.valueOf(gapCharacter);
496           if (skipint == null)
497           {
498             skipint = new int[]
499             { cdp[0], cdp[2] };
500           }
501           skipint[1] = cdp[2];
502         }
503         else
504         {
505           if (skipint != null)
506           {
507             // edit scontigs
508             skipint[0] = vismapping.shift(skipint[0]);
509             skipint[1] = vismapping.shift(skipint[1]);
510             for (vc = 0; vc < scontigs.length; )
511             {
512               if (scontigs[vc + 1] < skipint[0])
513               {
514                 // before skipint starts
515                 vc += 2;
516                 continue;
517               }
518               if (scontigs[vc]>skipint[1])
519               {
520                 // finished editing so
521                 break;
522               }
523               // Edit the contig list to include the skipped region which did not translate
524               int[] t;
525               // from : s1 e1 s2 e2 s3 e3
526               // to s:  s1 e1 s2 k0 k1 e2 s3 e3
527               // list increases by one unless one boundary (s2==k0 or e2==k1) matches, and decreases by one if skipint intersects whole visible contig 
528               if (scontigs[vc] <= skipint[0])
529               {
530                 if (skipint[0] == scontigs[vc])
531                 {
532                   // skipint at start of contig
533                   // shift the start of this contig
534                   if (scontigs[vc + 1] > skipint[1])
535                   {
536                     scontigs[vc] = skipint[1];
537                     vc+=2;
538                   }
539                   else
540                   {
541                     if (scontigs[vc+1]==skipint[1])
542                     {
543                       // remove the contig
544                       t = new int[scontigs.length - 2];
545                       if (vc > 0)
546                       {
547                         System.arraycopy(scontigs, 0, t, 0, vc - 1);
548                       }
549                       if (vc + 2 < t.length)
550                       {
551                         System.arraycopy(scontigs, vc + 2, t, vc, t.length
552                               - vc + 2);
553                       }
554                       scontigs=t;
555                     } else {
556                       // truncate contig to before the skipint region
557                       scontigs[vc+1] = skipint[0]-1;
558                       vc+=2;
559                     }
560                   }
561                 }
562                 else
563                 {
564                   // scontig starts before start of skipint
565                   if (scontigs[vc+1]<skipint[1]) {
566                     // skipint truncates end of scontig
567                     scontigs[vc+1] = skipint[0]-1; 
568                     vc+=2;
569                   } else {
570                     // divide region to new contigs
571                     t = new int[scontigs.length + 2];
572                     System.arraycopy(scontigs, 0, t, 0, vc +1);
573                     t[vc+1] = skipint[0];
574                     t[vc+2] = skipint[1];
575                     System.arraycopy(scontigs, vc + 1, t, vc+3, scontigs.length-(vc+1));
576                     scontigs=t;
577                     vc+=4;
578                   }
579                 }
580               }
581             }
582             skip.add(skipint);
583             skipint = null;
584           }
585           if (aa.equals("STOP"))
586           {
587             aa = starForStop ? "*" : "X";
588           }
589           resSize++;
590         }
591         // insert/delete gaps prior to this codon - if necessary
592         boolean findpos = true;
593         while (findpos)
594         {
595           // first ensure that the codons array is long enough.
596           codons.checkCodonFrameWidth(aspos);
597           // now check to see if we place the aa at the current aspos in the
598           // protein alignment
599           switch (Dna.compare_codonpos(cdp, codons.codons[aspos]))
600           {
601           case -1:
602             codons.insertAAGap(aspos, gapCharacter);
603             findpos = false;
604             break;
605           case +1:
606             // this aa appears after the aligned codons at aspos, so prefix it
607             // with a gap
608             aa = "" + gapCharacter + aa;
609             aspos++;
610             // if (aspos >= codons.aaWidth)
611             // codons.aaWidth = aspos + 1;
612             break; // check the next position for alignment
613           case 0:
614             // codon aligns at aspos position.
615             findpos = false;
616           }
617         }
618         // codon aligns with all other sequence residues found at aspos
619         protein.append(aa);
620         lastnpos = npos;
621         if (codons.codons[aspos] == null)
622         {
623           // mark this column as aligning to this aligned reading frame
624           codons.codons[aspos] = new int[]
625           { cdp[0], cdp[1], cdp[2] };
626         }
627         if (aspos >= codons.aaWidth)
628         {
629           // update maximum alignment width
630           // (we can do this without calling checkCodonFrameWidth because it was
631           // already done above)
632           codons.setAaWidth(aspos);
633         }
634         // ready for next translated reading frame alignment position (if any)
635         aspos++;
636       }
637     }
638     if (resSize > 0)
639     {
640       SequenceI newseq = new Sequence(selection.getName(),
641               protein.toString());
642       if (rf != 0)
643       {
644         if (jalview.bin.Cache.log!=null) {
645           jalview.bin.Cache.log.debug("trimming contigs for incomplete terminal codon.");
646         } else {
647           System.err.println("trimming contigs for incomplete terminal codon.");
648         }
649         // map and trim contigs to ORF region
650         vc = scontigs.length - 1;
651         lastnpos = vismapping.shift(lastnpos); // place npos in context of
652         // whole dna alignment (rather
653         // than visible contigs)
654         // incomplete ORF could be broken over one or two visible contig
655         // intervals.
656         while (vc >= 0 && scontigs[vc] > lastnpos)
657         {
658           if (vc > 0 && scontigs[vc - 1] > lastnpos)
659           {
660             vc -= 2;
661           }
662           else
663           {
664             // correct last interval in list.
665             scontigs[vc] = lastnpos;
666           }
667         }
668
669         if (vc > 0 && (vc + 1) < scontigs.length)
670         {
671           // truncate map list to just vc elements
672           int t[] = new int[vc + 1];
673           System.arraycopy(scontigs, 0, t, 0, vc + 1);
674           scontigs = t;
675         }
676         if (vc <= 0)
677           scontigs = null;
678       }
679       if (scontigs != null)
680       {
681         npos = 0;
682         // map scontigs to actual sequence positions on selection
683         for (vc = 0; vc < scontigs.length; vc += 2)
684         {
685           scontigs[vc] = selection.findPosition(scontigs[vc]); // not from 1!
686           scontigs[vc + 1] = selection.findPosition(scontigs[vc + 1]); // exclusive
687           if (scontigs[vc + 1] == selection.getEnd())
688             break;
689         }
690         // trim trailing empty intervals.
691         if ((vc + 2) < scontigs.length)
692         {
693           int t[] = new int[vc + 2];
694           System.arraycopy(scontigs, 0, t, 0, vc + 2);
695           scontigs = t;
696         }
697         /*
698          * delete intervals in scontigs which are not translated. 1. map skip
699          * into sequence position intervals 2. truncate existing ranges and add
700          * new ranges to exclude untranslated regions. if (skip.size()>0) {
701          * Vector narange = new Vector(); for (vc=0; vc<scontigs.length; vc++) {
702          * narange.addElement(new int[] {scontigs[vc]}); } int sint=0,iv[]; vc =
703          * 0; while (sint<skip.size()) { skipint = (int[]) skip.elementAt(sint);
704          * do { iv = (int[]) narange.elementAt(vc); if (iv[0]>=skipint[0] &&
705          * iv[0]<=skipint[1]) { if (iv[0]==skipint[0]) { // delete beginning of
706          * range } else { // truncate range and create new one if necessary iv =
707          * (int[]) narange.elementAt(vc+1); if (iv[0]<=skipint[1]) { // truncate
708          * range iv[0] = skipint[1]; } else { } } } else if (iv[0]<skipint[0]) {
709          * iv = (int[]) narange.elementAt(vc+1); } } while (iv[0]) } }
710          */
711         MapList map = new MapList(scontigs, new int[]
712         { 1, resSize }, 3, 1);
713
714         // update newseq as if it was generated as mapping from product
715
716         if (product != null)
717         {
718           newseq.setName(product.getSource() + "|"
719                   + product.getAccessionId());
720           if (product.getMap() != null)
721           {
722             // Mapping mp = product.getMap();
723             // newseq.setStart(mp.getPosition(scontigs[0]));
724             // newseq.setEnd(mp
725             // .getPosition(scontigs[scontigs.length - 1]));
726           }
727         }
728         transferCodedFeatures(selection, newseq, map, null, null);
729         SequenceI rseq = newseq.deriveSequence(); // construct a dataset
730         // sequence for our new
731         // peptide, regardless.
732         // store a mapping (this actually stores a mapping between the dataset
733         // sequences for the two sequences
734         codons.addMap(selection, rseq, map);
735         return rseq;
736       }
737     }
738     // register the mapping somehow
739     //
740     return null;
741   }
742
743   /**
744    * Given a peptide newly translated from a dna sequence, copy over and set any
745    * features on the peptide from the DNA. If featureTypes is null, all features
746    * on the dna sequence are searched (rather than just the displayed ones), and
747    * similarly for featureGroups.
748    * 
749    * @param dna
750    * @param pep
751    * @param map
752    * @param featureTypes
753    *          hash who's keys are the displayed feature type strings
754    * @param featureGroups
755    *          hash where keys are feature groups and values are Boolean objects
756    *          indicating if they are displayed.
757    */
758   private static void transferCodedFeatures(SequenceI dna, SequenceI pep,
759           MapList map, Hashtable featureTypes, Hashtable featureGroups)
760   {
761     SequenceFeature[] sf = (dna.getDatasetSequence()!=null ? dna.getDatasetSequence() : dna).getSequenceFeatures();
762     Boolean fgstate;
763     jalview.datamodel.DBRefEntry[] dnarefs = jalview.util.DBRefUtils
764             .selectRefs(dna.getDBRef(),
765                     jalview.datamodel.DBRefSource.DNACODINGDBS);
766     if (dnarefs != null)
767     {
768       // intersect with pep
769       for (int d = 0; d < dnarefs.length; d++)
770       {
771         Mapping mp = dnarefs[d].getMap();
772         if (mp != null)
773         {
774         }
775       }
776     }
777     if (sf != null)
778     {
779       for (int f = 0; f < sf.length; f++)
780       {
781         fgstate = (featureGroups == null) ? null : ((Boolean) featureGroups
782                 .get(sf[f].featureGroup));
783         if ((featureTypes == null || featureTypes.containsKey(sf[f]
784                 .getType())) && (fgstate == null || fgstate.booleanValue()))
785         {
786           if (FeatureProperties.isCodingFeature(null, sf[f].getType()))
787           {
788             // if (map.intersectsFrom(sf[f].begin, sf[f].end))
789             {
790
791             }
792           }
793         }
794       }
795     }
796   }
797 }