JAL-2920 revised TODO for JAL-3025
[jalview.git] / src / jalview / ws / dbsources / Uniprot.java
index 0a252b1..c311ea9 100644 (file)
  */
 package jalview.ws.dbsources;
 
+import jalview.bin.Cache;
+import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.DBRefSource;
 import jalview.datamodel.PDBEntry;
+import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
-import jalview.datamodel.UniprotEntry;
-import jalview.datamodel.UniprotFile;
-import jalview.ws.ebi.EBIFetchClient;
+import jalview.datamodel.xdb.uniprot.UniprotEntry;
+import jalview.datamodel.xdb.uniprot.UniprotFeature;
+import jalview.datamodel.xdb.uniprot.UniprotFile;
+import jalview.schemes.ResidueProperties;
+import jalview.util.StringUtils;
 import jalview.ws.seqfetcher.DbSourceProxyImpl;
 
-import java.io.File;
-import java.io.FileReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.io.Reader;
 import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Vector;
 
 import org.exolab.castor.mapping.Mapping;
@@ -48,11 +56,13 @@ import com.stevesoft.pat.Regex;
  */
 public class Uniprot extends DbSourceProxyImpl
 {
+  private static final String DEFAULT_UNIPROT_DOMAIN = "https://www.uniprot.org";
 
   private static final String BAR_DELIMITER = "|";
 
-  private static final String NEWLINE = "\n";
-
+  /*
+   * Castor mapping loaded from uniprot_mapping.xml
+   */
   private static Mapping map;
 
   /**
@@ -63,6 +73,11 @@ public class Uniprot extends DbSourceProxyImpl
     super();
   }
 
+  private String getDomain()
+  {
+    return Cache.getDefault("UNIPROT_DOMAIN", DEFAULT_UNIPROT_DOMAIN);
+  }
+
   /*
    * (non-Javadoc)
    * 
@@ -71,7 +86,7 @@ public class Uniprot extends DbSourceProxyImpl
   @Override
   public String getAccessionSeparator()
   {
-    return null; // ";";
+    return null;
   }
 
   /*
@@ -158,145 +173,249 @@ public class Uniprot extends DbSourceProxyImpl
       queries = queries.toUpperCase().replaceAll(
               "(UNIPROT\\|?|UNIPROT_|UNIREF\\d+_|UNIREF\\d+\\|?)", "");
       AlignmentI al = null;
-      EBIFetchClient ebi = new EBIFetchClient();
-      // uniprotxml parameter required since december 2007
-      // uniprotkb dbname changed introduced december 2008
-      File file = ebi.fetchDataAsFile("uniprotkb:" + queries, "uniprotxml",
-              null);
-      Vector<UniprotEntry> entries = getUniprotEntries(new FileReader(file));
+
+      String downloadstring = getDomain() + "/uniprot/" + queries
+              + ".xml";
+      URL url = null;
+      URLConnection urlconn = null;
+
+      url = new URL(downloadstring);
+      urlconn = url.openConnection();
+      InputStream istr = urlconn.getInputStream();
+      Vector<UniprotEntry> entries = getUniprotEntries(
+              new InputStreamReader(istr, "UTF-8"));
 
       if (entries != null)
       {
-        /*
-         * If Castor binding included sequence@length, we could guesstimate the
-         * size of buffer to hold the alignment
-         */
-        StringBuffer result = new StringBuffer(128);
-        // First, make the new sequences
+        ArrayList<SequenceI> seqs = new ArrayList<>();
         for (UniprotEntry entry : entries)
         {
-          StringBuilder name = constructSequenceFastaHeader(entry);
-
-          result.append(name).append(NEWLINE)
-                  .append(entry.getUniprotSequence().getContent())
-                  .append(NEWLINE);
+          seqs.add(uniprotEntryToSequenceI(entry));
         }
+        al = new Alignment(seqs.toArray(new SequenceI[0]));
 
-        // Then read in the features and apply them to the dataset
-        al = parseResult(result.toString());
-        if (al != null)
-        {
-          // Decorate the alignment with database entries.
-          addUniprotXrefs(al, entries);
-        }
-        else
-        {
-          results = result;
-        }
       }
       stopQuery();
       return al;
     } catch (Exception e)
     {
-      stopQuery();
       throw (e);
+    } finally
+    {
+      stopQuery();
     }
   }
 
   /**
-   * Construct a Fasta-format sequence header by concatenating the source,
-   * accession id(s) and name(s), delimited by '|', plus any protein names, now
-   * with space rather than bar delimiter
    * 
    * @param entry
-   * @return
+   *          UniprotEntry
+   * @return SequenceI instance created from the UniprotEntry instance
    */
-  public static StringBuilder constructSequenceFastaHeader(
-          UniprotEntry entry)
+  public SequenceI uniprotEntryToSequenceI(UniprotEntry entry)
   {
-    StringBuilder name = new StringBuilder(32);
-    name.append(">UniProt/Swiss-Prot");
+    String id = getUniprotEntryId(entry);
+    SequenceI sequence = new Sequence(id,
+            entry.getUniprotSequence().getContent());
+    sequence.setDescription(getUniprotEntryDescription(entry));
+
+    final String dbVersion = getDbVersion();
+    ArrayList<DBRefEntry> dbRefs = new ArrayList<>();
     for (String accessionId : entry.getAccession())
     {
-      name.append(BAR_DELIMITER);
-      name.append(accessionId);
+      DBRefEntry dbRef = new DBRefEntry(DBRefSource.UNIPROT, dbVersion,
+              accessionId);
+
+      // mark dbRef as a primary reference for this sequence
+      dbRefs.add(dbRef);
     }
-    for (String n : entry.getName())
+
+    Vector<PDBEntry> onlyPdbEntries = new Vector<>();
+    for (PDBEntry pdb : entry.getDbReference())
     {
-      name.append(BAR_DELIMITER);
-      name.append(n);
+      DBRefEntry dbr = new DBRefEntry();
+      dbr.setSource(pdb.getType());
+      dbr.setAccessionId(pdb.getId());
+      dbr.setVersion(DBRefSource.UNIPROT + ":" + dbVersion);
+      dbRefs.add(dbr);
+      if ("PDB".equals(pdb.getType()))
+      {
+        onlyPdbEntries.addElement(pdb);
+      }
+      if ("EMBL".equals(pdb.getType()))
+      {
+        // look for a CDS reference and add it, too.
+        String cdsId = (String) pdb.getProperty("protein sequence ID");
+        if (cdsId != null && cdsId.trim().length() > 0)
+        {
+          // remove version
+          String[] vrs = cdsId.split("\\.");
+          dbr = new DBRefEntry(DBRefSource.EMBLCDS, vrs.length > 1 ? vrs[1]
+                  : DBRefSource.UNIPROT + ":" + dbVersion, vrs[0]);
+          dbRefs.add(dbr);
+        }
+      }
+      if ("Ensembl".equals(pdb.getType()))
+      {
+        /*UniprotXML
+         * <dbReference type="Ensembl" id="ENST00000321556">
+        * <molecule id="Q9BXM7-1"/>
+        * <property type="protein sequence ID" value="ENSP00000364204"/>
+        * <property type="gene ID" value="ENSG00000158828"/>
+        * </dbReference> 
+         */
+        String cdsId = (String) pdb.getProperty("protein sequence ID");
+        if (cdsId != null && cdsId.trim().length() > 0)
+        {
+          dbr = new DBRefEntry(DBRefSource.ENSEMBL,
+                  DBRefSource.UNIPROT + ":" + dbVersion, cdsId.trim());
+          dbRefs.add(dbr);
+
+        }
+      }
     }
 
-    if (entry.getProtein() != null && entry.getProtein().getName() != null)
+    sequence.setPDBId(onlyPdbEntries);
+    if (entry.getFeature() != null)
     {
-      for (String nm : entry.getProtein().getName())
+      for (UniprotFeature uf : entry.getFeature())
       {
-        name.append(" ").append(nm);
+        SequenceFeature copy = new SequenceFeature(uf.getType(),
+                getDescription(uf), uf.getBegin(), uf.getEnd(), "Uniprot");
+        copy.setStatus(uf.getStatus());
+        sequence.addSequenceFeature(copy);
       }
     }
-    return name;
+    for (DBRefEntry dbr : dbRefs)
+    {
+      sequence.addDBRef(dbr);
+    }
+    return sequence;
   }
 
   /**
-   * add an ordered set of UniprotEntry objects to an ordered set of seuqences.
+   * Constructs a feature description from the description and (optionally)
+   * original and variant fields of the Uniprot XML feature
    * 
-   * @param al
-   *          - a sequence of n sequences
-   * @param entries
-   *          a list of n uniprot entries to be analysed.
+   * @param uf
+   * @return
    */
-  public void addUniprotXrefs(AlignmentI al, Vector<UniprotEntry> entries)
+  protected static String getDescription(UniprotFeature uf)
   {
-    final String dbVersion = getDbVersion();
-
-    for (int i = 0; i < entries.size(); i++)
+    String orig = uf.getOriginal();
+    List<String> variants = uf.getVariation();
+    StringBuilder sb = new StringBuilder();
+
+    /*
+     * append variant in standard format if present
+     * e.g. p.Arg59Lys
+     */
+    if (orig != null && !orig.isEmpty() && variants != null
+            && !variants.isEmpty())
     {
-      UniprotEntry entry = entries.elementAt(i);
-      Vector<PDBEntry> onlyPdbEntries = new Vector<PDBEntry>();
-      Vector<DBRefEntry> dbxrefs = new Vector<DBRefEntry>();
-
-      for (PDBEntry pdb : entry.getDbReference())
+      int p = 0;
+      for (String var : variants)
       {
-        DBRefEntry dbr = new DBRefEntry();
-        dbr.setSource(pdb.getType());
-        dbr.setAccessionId(pdb.getId());
-        dbr.setVersion(DBRefSource.UNIPROT + ":" + dbVersion);
-        dbxrefs.addElement(dbr);
-        if ("PDB".equals(pdb.getType()))
+        // TODO proper HGVS nomenclature for delins structural variations
+        // http://varnomen.hgvs.org/recommendations/protein/variant/delins/
+        // for now we are pragmatic - any orig/variant sequence longer than
+        // three characters is shown with single-character notation rather than
+        // three-letter notation
+        sb.append("p.");
+        if (orig.length() < 4)
         {
-          onlyPdbEntries.addElement(pdb);
+          for (int c = 0, clen = orig.length(); c < clen; c++)
+          {
+            char origchar = orig.charAt(c);
+            String orig3 = ResidueProperties.aa2Triplet.get("" + origchar);
+            sb.append(orig3 == null ? origchar
+                    : StringUtils.toSentenceCase(orig3));
+          }
+        }
+        else
+        {
+          sb.append(orig);
         }
-      }
 
-      SequenceI sq = al.getSequenceAt(i);
-      while (sq.getDatasetSequence() != null)
-      {
-        sq = sq.getDatasetSequence();
-      }
+        sb.append(Integer.toString(uf.getPosition()));
 
-      for (String accessionId : entry.getAccession())
-      {
-        /*
-         * add as uniprot whether retrieved from uniprot or uniprot_name
-         */
-        sq.addDBRef(new DBRefEntry(DBRefSource.UNIPROT, dbVersion,
-                accessionId));
+        if (var.length() < 4)
+        {
+          for (int c = 0, clen = var.length(); c < clen; c++)
+          {
+            char varchar = var.charAt(c);
+            String var3 = ResidueProperties.aa2Triplet.get("" + varchar);
+
+            sb.append(var3 != null ? StringUtils.toSentenceCase(var3)
+                    : "" + varchar);
+          }
+        }
+        else
+        {
+          sb.append(var);
+        }
+        if (++p != variants.size())
+        {
+          sb.append("\n");
+        }
+        else
+        {
+          sb.append(" ");
+        }
       }
+    }
+    String description = uf.getDescription();
+    if (description != null)
+    {
+      sb.append(description);
+    }
 
-      for (DBRefEntry dbRef : dbxrefs)
-      {
-        sq.addDBRef(dbRef);
-      }
-      sq.setPDBId(onlyPdbEntries);
-      if (entry.getFeature() != null)
+    return sb.toString();
+  }
+
+  /**
+   * 
+   * @param entry
+   *          UniportEntry
+   * @return protein name(s) delimited by a white space character
+   */
+  public static String getUniprotEntryDescription(UniprotEntry entry)
+  {
+    StringBuilder desc = new StringBuilder(32);
+    if (entry.getProtein() != null && entry.getProtein().getName() != null)
+    {
+      boolean first = true;
+      for (String nm : entry.getProtein().getName())
       {
-        for (SequenceFeature sf : entry.getFeature())
+        if (!first)
         {
-          sf.setFeatureGroup("Uniprot");
-          sq.addSequenceFeature(sf);
+          desc.append(" ");
         }
+        first = false;
+        desc.append(nm);
+      }
+    }
+    return desc.toString();
+  }
+
+  /**
+   *
+   * @param entry
+   *          UniprotEntry
+   * @return The accession id(s) and name(s) delimited by '|'.
+   */
+  public static String getUniprotEntryId(UniprotEntry entry)
+  {
+    StringBuilder name = new StringBuilder(32);
+    for (String n : entry.getName())
+    {
+      if (name.length() > 0)
+      {
+        name.append(BAR_DELIMITER);
       }
+      name.append(n);
     }
+    return name.toString();
   }
 
   /*
@@ -332,13 +451,4 @@ public class Uniprot extends DbSourceProxyImpl
   {
     return 0;
   }
-
-  @Override
-  public int getMaximumQueryCount()
-  {
-    // relocated this commented out code...
-    // addDbSourceProperty(DBRefSource.MULTIACC, new Integer(50));
-    // return 50;
-    return super.getMaximumQueryCount();
-  }
 }