JAL-2114 save 'raw' ENA location as a sequence feature attribute
[jalview.git] / src / jalview / datamodel / xdb / embl / EmblEntry.java
index d830130..50a262f 100644 (file)
@@ -29,10 +29,12 @@ import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.util.DBRefUtils;
+import jalview.util.DnaUtils;
 import jalview.util.MapList;
 import jalview.util.MappingUtils;
 import jalview.util.StringUtils;
 
+import java.util.Arrays;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
@@ -56,7 +58,15 @@ public class EmblEntry
 
   String accession;
 
-  String version;
+  String entryVersion;
+
+  String sequenceVersion;
+
+  String moleculeType;
+
+  String topology;
+
+  String sequenceLength;
 
   String taxDivision;
 
@@ -247,20 +257,20 @@ public class EmblEntry
   }
 
   /**
-   * @return the version
+   * @return the entry version
    */
-  public String getVersion()
+  public String getEntryVersion()
   {
-    return version;
+    return entryVersion;
   }
 
   /**
    * @param version
    *          the version to set
    */
-  public void setVersion(String version)
+  public void setEntryVersion(String version)
   {
-    this.version = version;
+    this.entryVersion = version;
   }
 
   /**
@@ -276,7 +286,8 @@ public class EmblEntry
     SequenceI dna = new Sequence(sourceDb + "|" + accession,
             sequence.getSequence());
     dna.setDescription(desc);
-    DBRefEntry retrievedref = new DBRefEntry(sourceDb, version, accession);
+    DBRefEntry retrievedref = new DBRefEntry(sourceDb,
+            getSequenceVersion(), accession);
     dna.addDBRef(retrievedref);
     // add map to indicate the sequence is a valid coordinate frame for the
     // dbref
@@ -400,7 +411,6 @@ public class EmblEntry
       }
     }
 
-    // SequenceI product = null;
     DBRefEntry protEMBLCDS = null;
     exon = MappingUtils.removeStartPositions(codonStart - 1, exon);
     boolean noProteinDbref = true;
@@ -436,7 +446,8 @@ public class EmblEntry
                   .println("Not allowing for additional stop codon at end of cDNA fragment... !");
           // this might occur for CDS sequences where no features are
           // marked.
-          exon = new int[] { dna.getStart() + (codonStart - 1), dna.getEnd() };
+          exon = new int[] { dna.getStart() + (codonStart - 1),
+              dna.getEnd() };
           map = new Mapping(product, exon, new int[] { 1, prseq.length() },
                   3, 1);
         }
@@ -469,16 +480,17 @@ public class EmblEntry
           // TODO should from range include stop codon even if not in protein
           // in order to include stop codon in CDS sequence (as done for
           // Ensembl)?
-          int[] cdsRanges = adjustForProteinLength(prseq.length(),
-                  exon);
-          map = new Mapping(product, cdsRanges, new int[] { 1, prseq.length() }, 3, 1);
+          int[] cdsRanges = adjustForProteinLength(prseq.length(), exon);
+          map = new Mapping(product, cdsRanges, new int[] { 1,
+              prseq.length() }, 3, 1);
           // reconstruct the EMBLCDS entry
           // TODO: this is only necessary when there codon annotation is
           // complete (I think JBPNote)
           DBRefEntry pcdnaref = new DBRefEntry();
           pcdnaref.setAccessionId(prid);
           pcdnaref.setSource(DBRefSource.EMBLCDS);
-          pcdnaref.setVersion(getVersion()); // same as parent EMBL version.
+          pcdnaref.setVersion(getSequenceVersion()); // same as parent EMBL
+                                                     // version.
           MapList mp = new MapList(new int[] { 1, prseq.length() },
                   new int[] { 1 + (codonStart - 1),
                       (codonStart - 1) + 3 * prseq.length() }, 1, 3);
@@ -498,6 +510,7 @@ public class EmblEntry
         SequenceFeature sf = makeCdsFeature(exon, xint, prname, prid, vals,
                 codonStart);
         sf.setType(feature.getName()); // "CDS"
+        sf.setEnaLocation(feature.getLocation());
         sf.setFeatureGroup(sourceDb);
         dna.addSequenceFeature(sf);
       }
@@ -536,8 +549,7 @@ public class EmblEntry
             if (proteinSeq == null)
             {
               proteinSeq = new Sequence(proteinSeqName,
-                      product
-                      .getSequenceAsString());
+                      product.getSequenceAsString());
               matcher.add(proteinSeq);
               peptides.add(proteinSeq);
             }
@@ -559,7 +571,7 @@ public class EmblEntry
           if (map != null)
           {
             Mapping pmap = new Mapping(dna, map.getMap().getInverse());
-            pref = new DBRefEntry(sourceDb, getVersion(),
+            pref = new DBRefEntry(sourceDb, getSequenceVersion(),
                     this.getAccession());
             pref.setMap(pmap);
             if (map.getTo() != null)
@@ -578,7 +590,7 @@ public class EmblEntry
           protEMBLCDS = new DBRefEntry();
           protEMBLCDS.setAccessionId(prid);
           protEMBLCDS.setSource(DBRefSource.EMBLCDSProduct);
-          protEMBLCDS.setVersion(getVersion());
+          protEMBLCDS.setVersion(getSequenceVersion());
           protEMBLCDS
                   .setMap(new Mapping(product, map.getMap().getInverse()));
         }
@@ -623,11 +635,11 @@ public class EmblEntry
     SequenceFeature sf = new SequenceFeature();
     sf.setBegin(Math.min(exons[exonStartIndex], exons[exonStartIndex + 1]));
     sf.setEnd(Math.max(exons[exonStartIndex], exons[exonStartIndex + 1]));
-    sf.setDescription(String.format(
-            "Exon %d for protein '%s' EMBLCDS:%s", exonNumber, proteinName,
-            proteinAccessionId));
+    sf.setDescription(String.format("Exon %d for protein '%s' EMBLCDS:%s",
+            exonNumber, proteinName, proteinAccessionId));
     sf.setPhase(String.valueOf(codonStart - 1));
-    sf.setStrand(exons[exonStartIndex] <= exons[exonStartIndex + 1] ? "+" : "-");
+    sf.setStrand(exons[exonStartIndex] <= exons[exonStartIndex + 1] ? "+"
+            : "-");
     sf.setValue(FeatureProperties.EXONPOS, exonNumber);
     sf.setValue(FeatureProperties.EXONPRODUCT, proteinName);
     if (!vals.isEmpty())
@@ -658,28 +670,31 @@ public class EmblEntry
    */
   protected int[] getCdsRanges(EmblFeature feature)
   {
-    if (feature.locations == null)
+    if (feature.location == null)
     {
       return new int[] {};
     }
-    int cdsBoundaryCount = 0; // count of all start/stop locations
-    int[][] cdsLocations = new int[feature.locations.size()][];
-    int locationNumber = 0;
-    for (EmblFeatureLocations loc : feature.locations)
-    {
-      int[] locationRanges = loc.getElementRanges(accession);
-      cdsLocations[locationNumber++] = locationRanges;
-      cdsBoundaryCount += locationRanges.length;
-    }
-    int[] cdsRanges = new int[cdsBoundaryCount];
-    int copyTo = 0;
-    for (int[] ranges : cdsLocations)
+    List<int[]> ranges = DnaUtils.parseLocation(feature.location);
+    return ranges == null ? new int[] {} : listToArray(ranges);
+  }
+
+  /**
+   * Converts a list of [start, end] ranges to a single array of [start, end,
+   * start, end ...]
+   * 
+   * @param ranges
+   * @return
+   */
+  int[] listToArray(List<int[]> ranges)
+  {
+    int[] result = new int[ranges.size() * 2];
+    int i = 0;
+    for (int[] range : ranges)
     {
-      System.arraycopy(ranges, 0, cdsRanges, copyTo, ranges.length);
-      copyTo += ranges.length;
+      result[i++] = range[0];
+      result[i++] = range[1];
     }
-    return cdsRanges;
-
+    return result;
   }
 
   /**
@@ -689,45 +704,129 @@ public class EmblEntry
    * @param exon
    * @return new exon
    */
-  private int[] adjustForProteinLength(int prlength, int[] exon)
+  static int[] adjustForProteinLength(int prlength, int[] exon)
   {
+    if (prlength <= 0 || exon == null)
+    {
+      return exon;
+    }
+    int desiredCdsLength = prlength * 3;
+    int exonLength = MappingUtils.getLength(Arrays.asList(exon));
+
+    /*
+     * assuming here exon might include stop codon in addition to protein codons
+     */
+    if (desiredCdsLength == exonLength
+            || desiredCdsLength == exonLength - 3)
+    {
+      return exon;
+    }
 
-    int origxon[], sxpos = -1, endxon = 0, cdslength = prlength * 3;
-    // first adjust range for codon start attribute
-    if (prlength >= 1 && exon != null)
+    int origxon[];
+    int sxpos = -1;
+    int endxon = 0;
+    origxon = new int[exon.length];
+    System.arraycopy(exon, 0, origxon, 0, exon.length);
+    int cdspos = 0;
+    for (int x = 0; x < exon.length; x += 2)
     {
-      origxon = new int[exon.length];
-      System.arraycopy(exon, 0, origxon, 0, exon.length);
-      int cdspos = 0;
-      for (int x = 0; x < exon.length && sxpos == -1; x += 2)
+      cdspos += Math.abs(exon[x + 1] - exon[x]) + 1;
+      if (desiredCdsLength <= cdspos)
       {
-        cdspos += Math.abs(exon[x + 1] - exon[x]) + 1;
-        if (cdslength <= cdspos)
+        // advanced beyond last codon.
+        sxpos = x;
+        if (desiredCdsLength != cdspos)
         {
-          // advanced beyond last codon.
-          sxpos = x;
-          if (cdslength != cdspos)
-          {
-            System.err
-                    .println("Truncating final exon interval on region by "
-                            + (cdspos - cdslength));
-          }
-          // locate the new end boundary of final exon as endxon
-          endxon = exon[x + 1] - cdspos + cdslength;
-          break;
+          // System.err
+          // .println("Truncating final exon interval on region by "
+          // + (cdspos - cdslength));
         }
-      }
 
-      if (sxpos != -1)
-      {
-        // and trim the exon interval set if necessary
-        int[] nxon = new int[sxpos + 2];
-        System.arraycopy(exon, 0, nxon, 0, sxpos + 2);
-        nxon[sxpos + 1] = endxon; // update the end boundary for the new exon
-                                  // set
-        exon = nxon;
+        /*
+         * shrink the final exon - reduce end position if forward
+         * strand, increase it if reverse
+         */
+        if (exon[x + 1] >= exon[x])
+        {
+          endxon = exon[x + 1] - cdspos + desiredCdsLength;
+        }
+        else
+        {
+          endxon = exon[x + 1] + cdspos - desiredCdsLength;
+        }
+        break;
       }
     }
+
+    if (sxpos != -1)
+    {
+      // and trim the exon interval set if necessary
+      int[] nxon = new int[sxpos + 2];
+      System.arraycopy(exon, 0, nxon, 0, sxpos + 2);
+      nxon[sxpos + 1] = endxon; // update the end boundary for the new exon
+                                // set
+      exon = nxon;
+    }
     return exon;
   }
+
+  public String getSequenceVersion()
+  {
+    return sequenceVersion;
+  }
+
+  public void setSequenceVersion(String sequenceVersion)
+  {
+    this.sequenceVersion = sequenceVersion;
+  }
+
+  public String getMoleculeType()
+  {
+    return moleculeType;
+  }
+
+  public void setMoleculeType(String moleculeType)
+  {
+    this.moleculeType = moleculeType;
+  }
+
+  public String getTopology()
+  {
+    return topology;
+  }
+
+  public void setTopology(String topology)
+  {
+    this.topology = topology;
+  }
+
+  public String getSequenceLength()
+  {
+    return sequenceLength;
+  }
+
+  public void setSequenceLength(String sequenceLength)
+  {
+    this.sequenceLength = sequenceLength;
+  }
+
+  public String getrCreated()
+  {
+    return rCreated;
+  }
+
+  public void setrCreated(String rCreated)
+  {
+    this.rCreated = rCreated;
+  }
+
+  public String getrLastUpdated()
+  {
+    return rLastUpdated;
+  }
+
+  public void setrLastUpdated(String rLastUpdated)
+  {
+    this.rLastUpdated = rLastUpdated;
+  }
 }