update author list in license for (JAL-826)
[jalview.git] / src / jalview / analysis / Dna.java
index 836c39e..9f3a8d0 100644 (file)
+/*\r
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7)\r
+ * Copyright (C) 2011 J Procter, AM Waterhouse, J Engelhardt, LM Lui, G Barton, M Clamp, S Searle\r
+ * \r
+ * This file is part of Jalview.\r
+ * \r
+ * Jalview is free software: you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License \r
+ * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\r
+ * \r
+ * Jalview is distributed in the hope that it will be useful, but \r
+ * WITHOUT ANY WARRANTY; without even the implied warranty \r
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR \r
+ * PURPOSE.  See the GNU General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.\r
+ */\r
 package jalview.analysis;\r
 \r
+import java.util.Enumeration;\r
 import java.util.Hashtable;\r
 import java.util.Vector;\r
 \r
+import jalview.datamodel.AlignedCodonFrame;\r
 import jalview.datamodel.Alignment;\r
 import jalview.datamodel.AlignmentAnnotation;\r
 import jalview.datamodel.AlignmentI;\r
 import jalview.datamodel.Annotation;\r
 import jalview.datamodel.ColumnSelection;\r
+import jalview.datamodel.DBRefEntry;\r
+import jalview.datamodel.FeatureProperties;\r
+import jalview.datamodel.Mapping;\r
 import jalview.datamodel.Sequence;\r
 import jalview.datamodel.SequenceFeature;\r
 import jalview.datamodel.SequenceI;\r
 import jalview.schemes.ResidueProperties;\r
 import jalview.util.MapList;\r
+import jalview.util.ShiftList;\r
 \r
-public class Dna {\r
+public class Dna\r
+{\r
   /**\r
    * \r
    * @param cdp1\r
    * @param cdp2\r
-   * @return -1 if cdp1 aligns before cdp2, 0 if in the same column or cdp2 is null, +1 if after cdp2\r
+   * @return -1 if cdp1 aligns before cdp2, 0 if in the same column or cdp2 is\r
+   *         null, +1 if after cdp2\r
    */\r
-  private static int compare_codonpos(int[] cdp1, int[] cdp2) {\r
-    if (cdp2==null || (cdp1[0]==cdp2[0] && cdp1[1] == cdp2[1] && cdp1[2] == cdp2[2]))\r
+  private static int compare_codonpos(int[] cdp1, int[] cdp2)\r
+  {\r
+    if (cdp2 == null\r
+            || (cdp1[0] == cdp2[0] && cdp1[1] == cdp2[1] && cdp1[2] == cdp2[2]))\r
       return 0;\r
-    if (cdp1[0]<cdp2[0] || cdp1[1]<cdp2[1] || cdp1[2]<cdp2[2])\r
-      return -1; // one base in cdp1 precedes the corresponding base in the other codon\r
-    return 1; // one base in cdp1 appears after the corresponding base in the other codon.\r
+    if (cdp1[0] < cdp2[0] || cdp1[1] < cdp2[1] || cdp1[2] < cdp2[2])\r
+      return -1; // one base in cdp1 precedes the corresponding base in the\r
+    // other codon\r
+    return 1; // one base in cdp1 appears after the corresponding base in the\r
+    // other codon.\r
   }\r
+\r
+  /**\r
+   * DNA->mapped protein sequence alignment translation given set of sequences\r
+   * 1. id distinct coding regions within selected region for each sequence 2.\r
+   * generate peptides based on inframe (or given) translation or (optionally\r
+   * and where specified) out of frame translations (annotated appropriately) 3.\r
+   * align peptides based on codon alignment\r
+   */\r
   /**\r
-   * create a new alignment of protein sequences\r
-   * by an inframe translation of the provided NA sequences\r
+   * id potential products from dna 1. search for distinct products within\r
+   * selected region for each selected sequence 2. group by associated DB type.\r
+   * 3. return as form for input into above function\r
+   */\r
+  /**\r
+   * \r
+   */\r
+  /**\r
+   * create a new alignment of protein sequences by an inframe translation of\r
+   * the provided NA sequences\r
+   * \r
    * @param selection\r
    * @param seqstring\r
    * @param viscontigs\r
    * @param gapCharacter\r
    * @param annotations\r
    * @param aWidth\r
+   * @param dataset\r
+   *          destination dataset for translated sequences and mappings\r
    * @return\r
    */\r
-  public static AlignmentI CdnaTranslate(SequenceI[] selection, String[] seqstring, int viscontigs[], char gapCharacter, \r
-      AlignmentAnnotation[] annotations, int aWidth) {\r
-    int s, sSize = selection.length;\r
-    SequenceI [] newSeq = new SequenceI[sSize];\r
-    int res, resSize;\r
-    StringBuffer protein;\r
-    String seq;\r
-\r
-    int[][] codons = new int[aWidth][]; // stores hash of subsequent positions for each codon start position in alignment\r
+  public static AlignmentI CdnaTranslate(SequenceI[] selection,\r
+          String[] seqstring, int viscontigs[], char gapCharacter,\r
+          AlignmentAnnotation[] annotations, int aWidth, Alignment dataset)\r
+  {\r
+    return CdnaTranslate(selection, seqstring, null, viscontigs,\r
+            gapCharacter, annotations, aWidth, dataset);\r
+  }\r
 \r
-    for (res=0;res<aWidth;res++)\r
-      codons[res]=null;\r
-    int aslen=0; // final width of aligned translated aa sequences\r
-    for(s=0; s<sSize; s++)\r
+  /**\r
+   * \r
+   * @param selection\r
+   * @param seqstring\r
+   * @param product\r
+   *          - array of DbRefEntry objects from which exon map in seqstring is\r
+   *          derived\r
+   * @param viscontigs\r
+   * @param gapCharacter\r
+   * @param annotations\r
+   * @param aWidth\r
+   * @param dataset\r
+   * @return\r
+   */\r
+  public static AlignmentI CdnaTranslate(SequenceI[] selection,\r
+          String[] seqstring, DBRefEntry[] product, int viscontigs[],\r
+          char gapCharacter, AlignmentAnnotation[] annotations, int aWidth,\r
+          Alignment dataset)\r
+  {\r
+    AlignedCodonFrame codons = new AlignedCodonFrame(aWidth); // stores hash of\r
+    // subsequent\r
+    // positions for\r
+    // each codon\r
+    // start position\r
+    // in alignment\r
+    int s, sSize = selection.length;\r
+    Vector pepseqs = new Vector();\r
+    for (s = 0; s < sSize; s++)\r
     {\r
-      int vc,scontigs[]=new int[viscontigs.length];\r
-\r
-      for (vc=0;vc<scontigs.length; vc+=2)\r
+      SequenceI newseq = translateCodingRegion(selection[s], seqstring[s],\r
+              viscontigs, codons, gapCharacter,\r
+              (product != null) ? product[s] : null); // possibly anonymous\r
+      // product\r
+      if (newseq != null)\r
       {\r
-        scontigs[vc]=selection[s].findPosition(viscontigs[vc]); // not from 1!\r
-        scontigs[vc+1]=selection[s].findPosition(viscontigs[vc+1]-1); // exclusive\r
-        if (scontigs[vc+1]==selection[s].getEnd())\r
-          break;\r
-      }\r
-      if ((vc+2)<scontigs.length) {\r
-        int t[] = new int[vc+2];\r
-        System.arraycopy(scontigs, 0, t, 0, vc+2);\r
-        scontigs = t;\r
+        pepseqs.addElement(newseq);\r
+        SequenceI ds = newseq;\r
+        while (ds.getDatasetSequence() != null)\r
+        {\r
+          ds = ds.getDatasetSequence();\r
+        }\r
+        dataset.addSequence(ds);\r
       }\r
-      protein = new StringBuffer();\r
-      seq = seqstring[s].replace('U', 'T');\r
-      char codon[]=new char[3];\r
-      int cdp[]=new int[3],rf=0,gf=0,nend,npos;\r
-      int aspos=0;\r
-      resSize=0;\r
-      for (npos=0,nend=seq.length(); npos<nend; npos++) {\r
-        if (!jalview.util.Comparison.isGap(seq.charAt(npos))) { \r
-          cdp[rf] = npos; // store position\r
-          codon[rf++]=seq.charAt(npos); // store base\r
-        }\r
-        // filled an RF yet ?\r
-        if (rf==3) {\r
-          String aa = ResidueProperties.codonTranslate(new String(codon));\r
-          rf=0;\r
-          if(aa==null)\r
-            aa=String.valueOf(gapCharacter);\r
-          else {\r
-            if(aa.equals("STOP"))\r
-            {\r
-              aa="X";\r
-            }\r
-            resSize++;\r
-          }\r
-          // insert/delete gaps prior to this codon - if necessary\r
-          boolean findpos=true;\r
-          while (findpos) \r
-          {\r
-            // first ensure that the codons array is long enough.\r
-            if (codons.length<=aslen+1) {\r
-              // probably never have to do this ?\r
-              int[][] c = new int[codons.length+10][];\r
-              for (int i=0; i<codons.length; i++) {\r
-                c[i] = codons[i];\r
-                codons[i]=null;\r
-              }\r
-              codons = c;\r
-            }\r
-            // now check to see if we place the aa at the current aspos in the protein alignment\r
-            switch (Dna.compare_codonpos(cdp, codons[aspos])) \r
+    }\r
+    if (codons.aaWidth == 0)\r
+      return null;\r
+    SequenceI[] newseqs = new SequenceI[pepseqs.size()];\r
+    pepseqs.copyInto(newseqs);\r
+    AlignmentI al = new Alignment(newseqs);\r
+    al.padGaps(); // ensure we look aligned.\r
+    al.setDataset(dataset);\r
+    translateAlignedAnnotations(annotations, al, codons);\r
+    al.addCodonFrame(codons);\r
+    return al;\r
+  }\r
+\r
+  /**\r
+   * fake the collection of DbRefs with associated exon mappings to identify if\r
+   * a translation would generate distinct product in the currently selected\r
+   * region.\r
+   * \r
+   * @param selection\r
+   * @param viscontigs\r
+   * @return\r
+   */\r
+  public static boolean canTranslate(SequenceI[] selection,\r
+          int viscontigs[])\r
+  {\r
+    for (int gd = 0; gd < selection.length; gd++)\r
+    {\r
+      SequenceI dna = selection[gd];\r
+      jalview.datamodel.DBRefEntry[] dnarefs = jalview.util.DBRefUtils\r
+              .selectRefs(dna.getDBRef(),\r
+                      jalview.datamodel.DBRefSource.DNACODINGDBS);\r
+      if (dnarefs != null)\r
+      {\r
+        // intersect with pep\r
+        // intersect with pep\r
+        Vector mappedrefs = new Vector();\r
+        DBRefEntry[] refs = dna.getDBRef();\r
+        for (int d = 0; d < refs.length; d++)\r
+        {\r
+          if (refs[d].getMap() != null && refs[d].getMap().getMap() != null\r
+                  && refs[d].getMap().getMap().getFromRatio() == 3\r
+                  && refs[d].getMap().getMap().getToRatio() == 1)\r
+          {\r
+            mappedrefs.addElement(refs[d]); // add translated protein maps\r
+          }\r
+        }\r
+        dnarefs = new DBRefEntry[mappedrefs.size()];\r
+        mappedrefs.copyInto(dnarefs);\r
+        for (int d = 0; d < dnarefs.length; d++)\r
+        {\r
+          Mapping mp = dnarefs[d].getMap();\r
+          if (mp != null)\r
+          {\r
+            for (int vc = 0; vc < viscontigs.length; vc += 2)\r
             {\r
-            case -1:\r
-              // this aa appears before the aligned codons at aspos - so shift them.\r
-              aslen++;\r
-              for (int sq=0;sq<s; sq++) {\r
-                newSeq[sq].insertCharAt(aspos, gapCharacter);\r
+              int[] mpr = mp.locateMappedRange(viscontigs[vc],\r
+                      viscontigs[vc + 1]);\r
+              if (mpr != null)\r
+              {\r
+                return true;\r
               }\r
-              System.arraycopy(codons, aspos, codons, aspos+1, aslen-aspos);\r
-              findpos=false;\r
-              break;\r
-            case +1:\r
-              // this aa appears after the aligned codons at aspos, so prefix it with a gap\r
-              aa = ""+gapCharacter+aa;\r
-              aspos++;\r
-              if (aspos>=aslen)\r
-                aslen=aspos+1;\r
-              break; // check the next position for alignment\r
-            case 0:\r
-              // codon aligns at aspos position.\r
-              findpos = false;\r
             }\r
           }\r
-          // codon aligns with all other sequence residues found at aspos\r
-          protein.append(aa);\r
-          if (codons[aspos]==null) \r
-          {\r
-            // mark this column as aligning to this aligned reading frame \r
-            codons[aspos] = new int[] { cdp[0], cdp[1], cdp[2] };\r
-          }\r
-          aspos++;\r
-          if (aspos>=aslen)\r
-            aslen=aspos+1;\r
         }\r
       }\r
-      if (resSize>0) \r
+    }\r
+    return false;\r
+  }\r
+\r
+  /**\r
+   * generate a set of translated protein products from annotated sequenceI\r
+   * \r
+   * @param selection\r
+   * @param viscontigs\r
+   * @param gapCharacter\r
+   * @param dataset\r
+   *          destination dataset for translated sequences\r
+   * @param annotations\r
+   * @param aWidth\r
+   * @return\r
+   */\r
+  public static AlignmentI CdnaTranslate(SequenceI[] selection,\r
+          int viscontigs[], char gapCharacter, Alignment dataset)\r
+  {\r
+    int alwidth = 0;\r
+    Vector cdnasqs = new Vector();\r
+    Vector cdnasqi = new Vector();\r
+    Vector cdnaprod = new Vector();\r
+    for (int gd = 0; gd < selection.length; gd++)\r
+    {\r
+      SequenceI dna = selection[gd];\r
+      jalview.datamodel.DBRefEntry[] dnarefs = jalview.util.DBRefUtils\r
+              .selectRefs(dna.getDBRef(),\r
+                      jalview.datamodel.DBRefSource.DNACODINGDBS);\r
+      if (dnarefs != null)\r
       {\r
-        newSeq[s] = new Sequence(selection[s].getName(),\r
-            protein.toString());\r
-        if (rf!=0) \r
-        {\r
-          jalview.bin.Cache.log.debug("trimming contigs for incomplete terminal codon.");\r
-          // trim contigs\r
-          vc=scontigs.length-1;\r
-          nend-=rf;\r
-          // incomplete ORF could be broken over one or two visible contig intervals.\r
-          while (vc>0 && scontigs[vc]>nend)\r
-          {\r
-            if (scontigs[vc-1]>nend) \r
-            {\r
-              vc-=2;\r
-            } else {\r
-              // correct last interval in list.\r
-              scontigs[vc]=nend;\r
-            }\r
+        // intersect with pep\r
+        Vector mappedrefs = new Vector();\r
+        DBRefEntry[] refs = dna.getDBRef();\r
+        for (int d = 0; d < refs.length; d++)\r
+        {\r
+          if (refs[d].getMap() != null && refs[d].getMap().getMap() != null\r
+                  && refs[d].getMap().getMap().getFromRatio() == 3\r
+                  && refs[d].getMap().getMap().getToRatio() == 1)\r
+          {\r
+            mappedrefs.addElement(refs[d]); // add translated protein maps\r
           }\r
-          if ((vc+2)<scontigs.length) {\r
-            // truncate map list\r
-            int t[] = new int[vc+1];\r
-            System.arraycopy(scontigs,0,t,0,vc+1);\r
-            scontigs=t;\r
+        }\r
+        dnarefs = new DBRefEntry[mappedrefs.size()];\r
+        mappedrefs.copyInto(dnarefs);\r
+        for (int d = 0; d < dnarefs.length; d++)\r
+        {\r
+          Mapping mp = dnarefs[d].getMap();\r
+          StringBuffer sqstr = new StringBuffer();\r
+          if (mp != null)\r
+          {\r
+            Mapping intersect = mp.intersectVisContigs(viscontigs);\r
+            // generate seqstring for this sequence based on mapping\r
+\r
+            if (sqstr.length() > alwidth)\r
+              alwidth = sqstr.length();\r
+            cdnasqs.addElement(sqstr.toString());\r
+            cdnasqi.addElement(dna);\r
+            cdnaprod.addElement(intersect);\r
           }\r
         }\r
-        MapList map = new MapList(scontigs, new int[] { 1, resSize },3,1); // TODO: store mapping on newSeq for linked DNA/Protein viewing.\r
       }\r
-      // register the mapping somehow\r
-      // \r
+      SequenceI[] cdna = new SequenceI[cdnasqs.size()];\r
+      DBRefEntry[] prods = new DBRefEntry[cdnaprod.size()];\r
+      String[] xons = new String[cdnasqs.size()];\r
+      cdnasqs.copyInto(xons);\r
+      cdnaprod.copyInto(prods);\r
+      cdnasqi.copyInto(cdna);\r
+      return CdnaTranslate(cdna, xons, prods, viscontigs, gapCharacter,\r
+              null, alwidth, dataset);\r
     }\r
-    if (aslen==0)\r
-      return null;\r
-    AlignmentI al = new Alignment(newSeq);\r
-    al.padGaps();  // ensure we look aligned.\r
-    al.setDataset(null);\r
-\r
+    return null;\r
+  }\r
 \r
-    ////////////////////////////////\r
+  /**\r
+   * translate na alignment annotations onto translated amino acid alignment al\r
+   * using codon mapping codons\r
+   * \r
+   * @param annotations\r
+   * @param al\r
+   * @param codons\r
+   */\r
+  public static void translateAlignedAnnotations(\r
+          AlignmentAnnotation[] annotations, AlignmentI al,\r
+          AlignedCodonFrame codons)\r
+  {\r
+    // //////////////////////////////\r
     // Copy annotations across\r
     //\r
     // Can only do this for columns with consecutive codons, or where\r
     // annotation is sequence associated.\r
-    \r
-    int pos,a,aSize;\r
-    if(annotations!=null)\r
+\r
+    int pos, a, aSize;\r
+    if (annotations != null)\r
     {\r
       for (int i = 0; i < annotations.length; i++)\r
       {\r
         // Skip any autogenerated annotation\r
-        if (annotations[i].autoCalculated) {\r
+        if (annotations[i].autoCalculated)\r
+        {\r
           continue;\r
         }\r
 \r
-        aSize = aslen; // aa alignment width.\r
-        jalview.datamodel.Annotation[] anots =\r
-          new jalview.datamodel.Annotation[aSize];\r
-        \r
-        for (a = 0; a < aSize; a++)\r
+        aSize = codons.getaaWidth(); // aa alignment width.\r
+        jalview.datamodel.Annotation[] anots = (annotations[i].annotations == null) ? null\r
+                : new jalview.datamodel.Annotation[aSize];\r
+        if (anots != null)\r
         {\r
-          // process through codon map.\r
-          if (codons[a]!=null && codons[a][0]==(codons[a][2]-2))\r
+          for (a = 0; a < aSize; a++)\r
           {\r
-            pos = codons[a][0];\r
-            if (annotations[i].annotations[pos] == null\r
-                || annotations[i].annotations[pos] == null)\r
-              continue;\r
-            \r
-            anots[a] = new Annotation(annotations[i].annotations[pos]);\r
+            // process through codon map.\r
+            if (codons.codons[a] != null\r
+                    && codons.codons[a][0] == (codons.codons[a][2] - 2))\r
+            {\r
+              anots[a] = getCodonAnnotation(codons.codons[a],\r
+                      annotations[i].annotations);\r
+            }\r
           }\r
         }\r
 \r
-        jalview.datamodel.AlignmentAnnotation aa\r
-        = new jalview.datamodel.AlignmentAnnotation(annotations[i].label,\r
-            annotations[i].description, anots);\r
+        jalview.datamodel.AlignmentAnnotation aa = new jalview.datamodel.AlignmentAnnotation(\r
+                annotations[i].label, annotations[i].description, anots);\r
+        aa.graph = annotations[i].graph;\r
+        aa.graphGroup = annotations[i].graphGroup;\r
+        aa.graphHeight = annotations[i].graphHeight;\r
+        if (annotations[i].getThreshold() != null)\r
+        {\r
+          aa.setThreshold(new jalview.datamodel.GraphLine(annotations[i]\r
+                  .getThreshold()));\r
+        }\r
+        if (annotations[i].hasScore)\r
+        {\r
+          aa.setScore(annotations[i].getScore());\r
+        }\r
+        if (annotations[i].sequenceRef != null)\r
+        {\r
+          SequenceI aaSeq = codons\r
+                  .getAaForDnaSeq(annotations[i].sequenceRef);\r
+          if (aaSeq != null)\r
+          {\r
+            // aa.compactAnnotationArray(); // throw away alignment annotation\r
+            // positioning\r
+            aa.setSequenceRef(aaSeq);\r
+            aa.createSequenceMapping(aaSeq, aaSeq.getStart(), true); // rebuild\r
+            // mapping\r
+            aa.adjustForAlignment();\r
+            aaSeq.addAlignmentAnnotation(aa);\r
+          }\r
+\r
+        }\r
         al.addAnnotation(aa);\r
       }\r
     }\r
-    return al;\r
+  }\r
+\r
+  private static Annotation getCodonAnnotation(int[] is,\r
+          Annotation[] annotations)\r
+  {\r
+    // Have a look at all the codon positions for annotation and put the first\r
+    // one found into the translated annotation pos.\r
+    int contrib = 0;\r
+    Annotation annot = null;\r
+    for (int p = 0; p < 3; p++)\r
+    {\r
+      if (annotations[is[p]] != null)\r
+      {\r
+        if (annot == null)\r
+        {\r
+          annot = new Annotation(annotations[is[p]]);\r
+          contrib = 1;\r
+        }\r
+        else\r
+        {\r
+          // merge with last\r
+          Annotation cpy = new Annotation(annotations[is[p]]);\r
+          if (annot.colour == null)\r
+          {\r
+            annot.colour = cpy.colour;\r
+          }\r
+          if (annot.description == null || annot.description.length() == 0)\r
+          {\r
+            annot.description = cpy.description;\r
+          }\r
+          if (annot.displayCharacter == null)\r
+          {\r
+            annot.displayCharacter = cpy.displayCharacter;\r
+          }\r
+          if (annot.secondaryStructure == 0)\r
+          {\r
+            annot.secondaryStructure = cpy.secondaryStructure;\r
+          }\r
+          annot.value += cpy.value;\r
+          contrib++;\r
+        }\r
+      }\r
+    }\r
+    if (contrib > 1)\r
+    {\r
+      annot.value /= (float) contrib;\r
+    }\r
+    return annot;\r
+  }\r
+\r
+  /**\r
+   * Translate a na sequence\r
+   * \r
+   * @param selection\r
+   *          sequence displayed under viscontigs visible columns\r
+   * @param seqstring\r
+   *          ORF read in some global alignment reference frame\r
+   * @param viscontigs\r
+   *          mapping from global reference frame to visible seqstring ORF read\r
+   * @param codons\r
+   *          Definition of global ORF alignment reference frame\r
+   * @param gapCharacter\r
+   * @param newSeq\r
+   * @return sequence ready to be added to alignment.\r
+   */\r
+  public static SequenceI translateCodingRegion(SequenceI selection,\r
+          String seqstring, int[] viscontigs, AlignedCodonFrame codons,\r
+          char gapCharacter, DBRefEntry product)\r
+  {\r
+    Vector skip = new Vector();\r
+    int skipint[] = null;\r
+    ShiftList vismapping = new ShiftList(); // map from viscontigs to seqstring\r
+    // intervals\r
+    int vc, scontigs[] = new int[viscontigs.length];\r
+    int npos = 0;\r
+    for (vc = 0; vc < viscontigs.length; vc += 2)\r
+    {\r
+      if (vc == 0)\r
+      {\r
+        vismapping.addShift(npos, viscontigs[vc]);\r
+      }\r
+      else\r
+      {\r
+        // hidden region\r
+        vismapping.addShift(npos, viscontigs[vc] - viscontigs[vc - 1] + 1);\r
+      }\r
+      scontigs[vc] = viscontigs[vc];\r
+      scontigs[vc + 1] = viscontigs[vc + 1];\r
+    }\r
+\r
+    StringBuffer protein = new StringBuffer();\r
+    String seq = seqstring.replace('U', 'T');\r
+    char codon[] = new char[3];\r
+    int cdp[] = new int[3], rf = 0, lastnpos = 0, nend;\r
+    int aspos = 0;\r
+    int resSize = 0;\r
+    for (npos = 0, nend = seq.length(); npos < nend; npos++)\r
+    {\r
+      if (!jalview.util.Comparison.isGap(seq.charAt(npos)))\r
+      {\r
+        cdp[rf] = npos; // store position\r
+        codon[rf++] = seq.charAt(npos); // store base\r
+      }\r
+      // filled an RF yet ?\r
+      if (rf == 3)\r
+      {\r
+        String aa = ResidueProperties.codonTranslate(new String(codon));\r
+        rf = 0;\r
+        if (aa == null)\r
+        {\r
+          aa = String.valueOf(gapCharacter);\r
+          if (skipint == null)\r
+          {\r
+            skipint = new int[]\r
+            { cdp[0], cdp[2] };\r
+          }\r
+          skipint[1] = cdp[2];\r
+        }\r
+        else\r
+        {\r
+          if (skipint != null)\r
+          {\r
+            // edit scontigs\r
+            skipint[0] = vismapping.shift(skipint[0]);\r
+            skipint[1] = vismapping.shift(skipint[1]);\r
+            for (vc = 0; vc < scontigs.length; vc += 2)\r
+            {\r
+              if (scontigs[vc + 1] < skipint[0])\r
+              {\r
+                continue;\r
+              }\r
+              if (scontigs[vc] <= skipint[0])\r
+              {\r
+                if (skipint[0] == scontigs[vc])\r
+                {\r
+\r
+                }\r
+                else\r
+                {\r
+                  int[] t = new int[scontigs.length + 2];\r
+                  System.arraycopy(scontigs, 0, t, 0, vc - 1);\r
+                  // scontigs[vc]; //\r
+                }\r
+              }\r
+            }\r
+            skip.addElement(skipint);\r
+            skipint = null;\r
+          }\r
+          if (aa.equals("STOP"))\r
+          {\r
+            aa = "X";\r
+          }\r
+          resSize++;\r
+        }\r
+        // insert/delete gaps prior to this codon - if necessary\r
+        boolean findpos = true;\r
+        while (findpos)\r
+        {\r
+          // first ensure that the codons array is long enough.\r
+          codons.checkCodonFrameWidth(aspos);\r
+          // now check to see if we place the aa at the current aspos in the\r
+          // protein alignment\r
+          switch (Dna.compare_codonpos(cdp, codons.codons[aspos]))\r
+          {\r
+          case -1:\r
+            codons.insertAAGap(aspos, gapCharacter);\r
+            findpos = false;\r
+            break;\r
+          case +1:\r
+            // this aa appears after the aligned codons at aspos, so prefix it\r
+            // with a gap\r
+            aa = "" + gapCharacter + aa;\r
+            aspos++;\r
+            // if (aspos >= codons.aaWidth)\r
+            // codons.aaWidth = aspos + 1;\r
+            break; // check the next position for alignment\r
+          case 0:\r
+            // codon aligns at aspos position.\r
+            findpos = false;\r
+          }\r
+        }\r
+        // codon aligns with all other sequence residues found at aspos\r
+        protein.append(aa);\r
+        lastnpos = npos;\r
+        if (codons.codons[aspos] == null)\r
+        {\r
+          // mark this column as aligning to this aligned reading frame\r
+          codons.codons[aspos] = new int[]\r
+          { cdp[0], cdp[1], cdp[2] };\r
+        }\r
+        if (aspos >= codons.aaWidth)\r
+        {\r
+          // update maximum alignment width\r
+          // (we can do this without calling checkCodonFrameWidth because it was\r
+          // already done above)\r
+          codons.setAaWidth(aspos);\r
+        }\r
+        // ready for next translated reading frame alignment position (if any)\r
+        aspos++;\r
+      }\r
+    }\r
+    if (resSize > 0)\r
+    {\r
+      SequenceI newseq = new Sequence(selection.getName(),\r
+              protein.toString());\r
+      if (rf != 0)\r
+      {\r
+        jalview.bin.Cache.log\r
+                .debug("trimming contigs for incomplete terminal codon.");\r
+        // map and trim contigs to ORF region\r
+        vc = scontigs.length - 1;\r
+        lastnpos = vismapping.shift(lastnpos); // place npos in context of\r
+        // whole dna alignment (rather\r
+        // than visible contigs)\r
+        // incomplete ORF could be broken over one or two visible contig\r
+        // intervals.\r
+        while (vc >= 0 && scontigs[vc] > lastnpos)\r
+        {\r
+          if (vc > 0 && scontigs[vc - 1] > lastnpos)\r
+          {\r
+            vc -= 2;\r
+          }\r
+          else\r
+          {\r
+            // correct last interval in list.\r
+            scontigs[vc] = lastnpos;\r
+          }\r
+        }\r
+\r
+        if (vc > 0 && (vc + 1) < scontigs.length)\r
+        {\r
+          // truncate map list to just vc elements\r
+          int t[] = new int[vc + 1];\r
+          System.arraycopy(scontigs, 0, t, 0, vc + 1);\r
+          scontigs = t;\r
+        }\r
+        if (vc <= 0)\r
+          scontigs = null;\r
+      }\r
+      if (scontigs != null)\r
+      {\r
+        npos = 0;\r
+        // map scontigs to actual sequence positions on selection\r
+        for (vc = 0; vc < scontigs.length; vc += 2)\r
+        {\r
+          scontigs[vc] = selection.findPosition(scontigs[vc]); // not from 1!\r
+          scontigs[vc + 1] = selection.findPosition(scontigs[vc + 1]); // exclusive\r
+          if (scontigs[vc + 1] == selection.getEnd())\r
+            break;\r
+        }\r
+        // trim trailing empty intervals.\r
+        if ((vc + 2) < scontigs.length)\r
+        {\r
+          int t[] = new int[vc + 2];\r
+          System.arraycopy(scontigs, 0, t, 0, vc + 2);\r
+          scontigs = t;\r
+        }\r
+        /*\r
+         * delete intervals in scontigs which are not translated. 1. map skip\r
+         * into sequence position intervals 2. truncate existing ranges and add\r
+         * new ranges to exclude untranslated regions. if (skip.size()>0) {\r
+         * Vector narange = new Vector(); for (vc=0; vc<scontigs.length; vc++) {\r
+         * narange.addElement(new int[] {scontigs[vc]}); } int sint=0,iv[]; vc =\r
+         * 0; while (sint<skip.size()) { skipint = (int[]) skip.elementAt(sint);\r
+         * do { iv = (int[]) narange.elementAt(vc); if (iv[0]>=skipint[0] &&\r
+         * iv[0]<=skipint[1]) { if (iv[0]==skipint[0]) { // delete beginning of\r
+         * range } else { // truncate range and create new one if necessary iv =\r
+         * (int[]) narange.elementAt(vc+1); if (iv[0]<=skipint[1]) { // truncate\r
+         * range iv[0] = skipint[1]; } else { } } } else if (iv[0]<skipint[0]) {\r
+         * iv = (int[]) narange.elementAt(vc+1); } } while (iv[0]) } }\r
+         */\r
+        MapList map = new MapList(scontigs, new int[]\r
+        { 1, resSize }, 3, 1);\r
+\r
+        // update newseq as if it was generated as mapping from product\r
+\r
+        if (product != null)\r
+        {\r
+          newseq.setName(product.getSource() + "|"\r
+                  + product.getAccessionId());\r
+          if (product.getMap() != null)\r
+          {\r
+            // Mapping mp = product.getMap();\r
+            // newseq.setStart(mp.getPosition(scontigs[0]));\r
+            // newseq.setEnd(mp\r
+            // .getPosition(scontigs[scontigs.length - 1]));\r
+          }\r
+        }\r
+        transferCodedFeatures(selection, newseq, map, null, null);\r
+        SequenceI rseq = newseq.deriveSequence(); // construct a dataset\r
+        // sequence for our new\r
+        // peptide, regardless.\r
+        // store a mapping (this actually stores a mapping between the dataset\r
+        // sequences for the two sequences\r
+        codons.addMap(selection, rseq, map);\r
+        return rseq;\r
+      }\r
+    }\r
+    // register the mapping somehow\r
+    //\r
+    return null;\r
+  }\r
+\r
+  /**\r
+   * Given a peptide newly translated from a dna sequence, copy over and set any\r
+   * features on the peptide from the DNA. If featureTypes is null, all features\r
+   * on the dna sequence are searched (rather than just the displayed ones), and\r
+   * similarly for featureGroups.\r
+   * \r
+   * @param dna\r
+   * @param pep\r
+   * @param map\r
+   * @param featureTypes\r
+   *          hash who's keys are the displayed feature type strings\r
+   * @param featureGroups\r
+   *          hash where keys are feature groups and values are Boolean objects\r
+   *          indicating if they are displayed.\r
+   */\r
+  private static void transferCodedFeatures(SequenceI dna, SequenceI pep,\r
+          MapList map, Hashtable featureTypes, Hashtable featureGroups)\r
+  {\r
+    SequenceFeature[] sf = dna.getDatasetSequence().getSequenceFeatures();\r
+    Boolean fgstate;\r
+    jalview.datamodel.DBRefEntry[] dnarefs = jalview.util.DBRefUtils\r
+            .selectRefs(dna.getDBRef(),\r
+                    jalview.datamodel.DBRefSource.DNACODINGDBS);\r
+    if (dnarefs != null)\r
+    {\r
+      // intersect with pep\r
+      for (int d = 0; d < dnarefs.length; d++)\r
+      {\r
+        Mapping mp = dnarefs[d].getMap();\r
+        if (mp != null)\r
+        {\r
+        }\r
+      }\r
+    }\r
+    if (sf != null)\r
+    {\r
+      for (int f = 0; f < sf.length; f++)\r
+      {\r
+        fgstate = (featureGroups == null) ? null : ((Boolean) featureGroups\r
+                .get(sf[f].featureGroup));\r
+        if ((featureTypes == null || featureTypes.containsKey(sf[f]\r
+                .getType())) && (fgstate == null || fgstate.booleanValue()))\r
+        {\r
+          if (FeatureProperties.isCodingFeature(null, sf[f].getType()))\r
+          {\r
+            // if (map.intersectsFrom(sf[f].begin, sf[f].end))\r
+            {\r
+\r
+            }\r
+          }\r
+        }\r
+      }\r
+    }\r
   }\r
 }\r