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