Merge branch 'releases/Release_2_10_4_Branch' into develop
[jalview.git] / src / jalview / ext / ensembl / EnsemblLookup.java
index 0ddef2b..e55605d 100644 (file)
  */
 package jalview.ext.ensembl;
 
+import jalview.bin.Cache;
 import jalview.datamodel.AlignmentI;
+import jalview.datamodel.GeneLociI;
+import jalview.util.MapList;
 
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
 import org.json.simple.JSONObject;
@@ -34,14 +38,15 @@ import org.json.simple.parser.JSONParser;
 import org.json.simple.parser.ParseException;
 
 /**
- * A client for the Ensembl lookup REST endpoint, used to find the gene
- * identifier given a gene, transcript or protein identifier.
+ * A client for the Ensembl /lookup REST endpoint, used to find the gene
+ * identifier given a gene, transcript or protein identifier, or to extract the
+ * species or chromosomal coordinates from the same service response
  * 
  * @author gmcarstairs
  */
 public class EnsemblLookup extends EnsemblRestClient
 {
-
+  private static final String SPECIES = "species";
   private static final String OBJECT_TYPE_TRANSLATION = "Translation";
   private static final String PARENT = "Parent";
   private static final String OBJECT_TYPE_TRANSCRIPT = "Transcript";
@@ -136,27 +141,49 @@ public class EnsemblLookup extends EnsemblRestClient
   }
 
   /**
-   * Returns the gene id related to the given identifier, which may be for a
-   * gene, transcript or protein
+   * Returns the gene id related to the given identifier (which may be for a
+   * gene, transcript or protein)
    * 
    * @param identifier
    * @return
    */
   public String getGeneId(String identifier)
   {
-    return getGeneId(identifier, null);
+    return parseGeneId(getResult(identifier, null));
   }
 
   /**
-   * Calls the Ensembl lookup REST endpoint and retrieves the 'Parent' for the
+   * Calls the Ensembl lookup REST endpoint and retrieves the 'species' for the
    * given identifier, or null if not found
    * 
    * @param identifier
+   * @return
+   */
+  public String getSpecies(String identifier)
+  {
+    String species = null;
+    JSONObject json = getResult(identifier, null);
+    if (json != null)
+    {
+      Object o = json.get(SPECIES);
+      if (o != null)
+      {
+        species = o.toString();
+      }
+    }
+    return species;
+  }
+
+  /**
+   * Calls the /lookup/id rest service and returns the response as a JSONObject,
+   * or null if any error
+   * 
+   * @param identifier
    * @param objectType
    *          (optional)
    * @return
    */
-  public String getGeneId(String identifier, String objectType)
+  protected JSONObject getResult(String identifier, String objectType)
   {
     List<String> ids = Arrays.asList(new String[] { identifier });
 
@@ -179,10 +206,11 @@ public class EnsemblLookup extends EnsemblRestClient
       {
         br = getHttpResponse(url, ids);
       }
-      return br == null ? null : parseResponse(br);
-    } catch (IOException e)
+      return br == null ? null : (JSONObject) (new JSONParser().parse(br));
+    } catch (IOException | ParseException e)
     {
-      // ignore
+      System.err.println("Error parsing " + identifier + " lookup response "
+              + e.getMessage());
       return null;
     } finally
     {
@@ -203,41 +231,119 @@ public class EnsemblLookup extends EnsemblRestClient
    * Parses the JSON response and returns the gene identifier, or null if not
    * found. If the returned object_type is Gene, returns the id, if Transcript
    * returns the Parent. If it is Translation (peptide identifier), then the
-   * Parent is the transcript identifier, so we redo the search with this value.
+   * Parent is the transcript identifier, so we redo the search with this value,
+   * specifying that object_type should be Transcript.
    * 
-   * @param br
+   * @param jsonObject
    * @return
-   * @throws IOException
    */
-  protected String parseResponse(BufferedReader br) throws IOException
+  protected String parseGeneId(JSONObject json)
   {
+    if (json == null)
+    {
+      // e.g. lookup failed with 404 not found
+      return null;
+    }
+
     String geneId = null;
-    JSONParser jp = new JSONParser();
+    String type = json.get(OBJECT_TYPE).toString();
+    if (OBJECT_TYPE_GENE.equalsIgnoreCase(type))
+    {
+      // got the gene - just returns its id
+      geneId = json.get(JSON_ID).toString();
+    }
+    else if (OBJECT_TYPE_TRANSCRIPT.equalsIgnoreCase(type))
+    {
+      // got the transcript - return its (Gene) Parent
+      geneId = json.get(PARENT).toString();
+    }
+    else if (OBJECT_TYPE_TRANSLATION.equalsIgnoreCase(type))
+    {
+      // got the protein - look up its Parent, restricted to type Transcript
+      String transcriptId = json.get(PARENT).toString();
+      geneId = parseGeneId(getResult(transcriptId, OBJECT_TYPE_TRANSCRIPT));
+    }
+
+    return geneId;
+  }
+
+  /**
+   * Calls the /lookup/id rest service for the given id, and if successful,
+   * parses and returns the gene's chromosomal coordinates
+   * 
+   * @param geneId
+   * @return
+   */
+  public GeneLociI getGeneLoci(String geneId)
+  {
+    return parseGeneLoci(getResult(geneId, OBJECT_TYPE_GENE));
+  }
+
+  /**
+   * Parses the /lookup/id response for species, asssembly_name,
+   * seq_region_name, start, end and returns an object that wraps them, or null
+   * if unsuccessful
+   * 
+   * @param json
+   * @return
+   */
+  GeneLociI parseGeneLoci(JSONObject json)
+  {
+    if (json == null)
+    {
+      return null;
+    }
+
     try
     {
-      JSONObject val = (JSONObject) jp.parse(br);
-      String type = val.get(OBJECT_TYPE).toString();
-      if (OBJECT_TYPE_GENE.equalsIgnoreCase(type))
-      {
-        // got the gene - just returns its id
-        geneId = val.get(ID).toString();
-      }
-      else if (OBJECT_TYPE_TRANSCRIPT.equalsIgnoreCase(type))
-      {
-        // got the transcript - return its (Gene) Parent
-        geneId = val.get(PARENT).toString();
-      }
-      else if (OBJECT_TYPE_TRANSLATION.equalsIgnoreCase(type))
+      final String species = json.get("species").toString();
+      final String assembly = json.get("assembly_name").toString();
+      final String chromosome = json.get("seq_region_name").toString();
+      String strand = json.get("strand").toString();
+      int start = Integer.parseInt(json.get("start").toString());
+      int end = Integer.parseInt(json.get("end").toString());
+      int fromEnd = end - start + 1;
+      boolean reverseStrand = "-1".equals(strand);
+      int toStart = reverseStrand ? end : start;
+      int toEnd = reverseStrand ? start : end;
+      List<int[]> fromRange = Collections.singletonList(new int[] { 1,
+          fromEnd });
+      List<int[]> toRange = Collections.singletonList(new int[] { toStart,
+          toEnd });
+      final MapList map = new MapList(fromRange, toRange, 1, 1);
+      return new GeneLociI()
       {
-        // got the protein - get its Parent, restricted to type Transcript
-        String transcriptId = val.get(PARENT).toString();
-        geneId = getGeneId(transcriptId, OBJECT_TYPE_TRANSCRIPT);
-      }
-    } catch (ParseException e)
+
+        @Override
+        public String getSpeciesId()
+        {
+          return species == null ? "" : species;
+        }
+
+        @Override
+        public String getAssemblyId()
+        {
+          return assembly;
+        }
+
+        @Override
+        public String getChromosomeId()
+        {
+          return chromosome;
+        }
+
+        @Override
+        public MapList getMap()
+        {
+          return map;
+        }
+      };
+    } catch (NullPointerException | NumberFormatException e)
     {
-      // ignore
+      Cache.log.error("Error looking up gene loci: " + e.getMessage());
+      e.printStackTrace();
     }
-    return geneId;
+    return null;
   }
 
 }