reconciled with develop
[jalview.git] / src / jalview / ext / ensembl / EnsemblLookup.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ 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.ext.ensembl;
22
23 import jalview.bin.Cache;
24 import jalview.datamodel.AlignmentI;
25 import jalview.datamodel.GeneLociI;
26 import jalview.util.MapList;
27
28 import java.io.IOException;
29 import java.net.MalformedURLException;
30 import java.net.URL;
31 import java.util.Arrays;
32 import java.util.Collections;
33 import java.util.List;
34 import java.util.Map;
35
36 import org.json.simple.parser.ParseException;
37
38 /**
39  * A client for the Ensembl /lookup REST endpoint, used to find the gene
40  * identifier given a gene, transcript or protein identifier, or to extract the
41  * species or chromosomal coordinates from the same service response
42  * 
43  * @author gmcarstairs
44  */
45 public class EnsemblLookup extends EnsemblRestClient
46 {
47   private static final String SPECIES = "species";
48
49   /**
50    * Default constructor (to use rest.ensembl.org)
51    */
52   public EnsemblLookup()
53   {
54     super();
55   }
56
57   /**
58    * Constructor given the target domain to fetch data from
59    * 
60    * @param
61    */
62   public EnsemblLookup(String d)
63   {
64     super(d);
65   }
66
67   @Override
68   public String getDbName()
69   {
70     return "ENSEMBL";
71   }
72
73   @Override
74   public AlignmentI getSequenceRecords(String queries) throws Exception
75   {
76     return null;
77   }
78
79   @Override
80   protected URL getUrl(List<String> ids) throws MalformedURLException
81   {
82     String identifier = ids.get(0);
83     return getUrl(identifier, null);
84   }
85
86   /**
87    * Gets the url for lookup of the given identifier, optionally with objectType
88    * also specified in the request
89    * 
90    * @param identifier
91    * @param objectType
92    * @return
93    */
94   protected URL getUrl(String identifier, String objectType)
95   {
96     String url = getDomain() + "/lookup/id/" + identifier
97             + CONTENT_TYPE_JSON;
98     if (objectType != null)
99     {
100       url += "&" + OBJECT_TYPE + "=" + objectType;
101     }
102
103     try
104     {
105       return new URL(url);
106     } catch (MalformedURLException e)
107     {
108       return null;
109     }
110   }
111
112   @Override
113   protected boolean useGetRequest()
114   {
115     return true;
116   }
117
118   /**
119    * Returns the gene id related to the given identifier (which may be for a
120    * gene, transcript or protein), or null if none is found
121    * 
122    * @param identifier
123    * @return
124    */
125   public String getGeneId(String identifier)
126   {
127     return getGeneId(identifier, null);
128   }
129
130   /**
131    * Returns the gene id related to the given identifier (which may be for a
132    * gene, transcript or protein), or null if none is found
133    * 
134    * @param identifier
135    * @param objectType
136    * @return
137    */
138   public String getGeneId(String identifier, String objectType)
139   {
140     return parseGeneId(getResult(identifier, objectType));
141   }
142
143   /**
144    * Parses the JSON response and returns the gene identifier, or null if not
145    * found. If the returned object_type is Gene, returns the id, if Transcript
146    * returns the Parent. If it is Translation (peptide identifier), then the
147    * Parent is the transcript identifier, so we redo the search with this value.
148    * 
149    * @param br
150    * @return
151    */
152   protected String parseGeneId(Map<String, Object> val)
153   {
154     if (val == null)
155     {
156       return null;
157     }
158     String geneId = null;
159     String type = val.get(OBJECT_TYPE).toString();
160     if (OBJECT_TYPE_GENE.equalsIgnoreCase(type))
161     {
162       // got the gene - just returns its id
163       geneId = val.get(JSON_ID).toString();
164     }
165     else if (OBJECT_TYPE_TRANSCRIPT.equalsIgnoreCase(type))
166     {
167       // got the transcript - return its (Gene) Parent
168       geneId = val.get(PARENT).toString();
169     }
170     else if (OBJECT_TYPE_TRANSLATION.equalsIgnoreCase(type))
171     {
172       // got the protein - get its Parent, restricted to type Transcript
173       String transcriptId = val.get(PARENT).toString();
174       geneId = getGeneId(transcriptId, OBJECT_TYPE_TRANSCRIPT);
175     }
176
177     return geneId;
178   }
179
180   /**
181    * Calls the Ensembl lookup REST endpoint and retrieves the 'species' for the
182    * given identifier, or null if not found
183    * 
184    * @param identifier
185    * @return
186    */
187   public String getSpecies(String identifier)
188   {
189     String species = null;
190     Map<String, Object> json = getResult(identifier, null);
191     if (json != null)
192     {
193       Object o = json.get(SPECIES);
194       if (o != null)
195       {
196         species = o.toString();
197       }
198     }
199     return species;
200   }
201
202   /**
203    * Calls the /lookup/id rest service and returns the response as a Map<String, Object>,
204    * or null if any error
205    * 
206    * @param identifier
207    * @param objectType
208    *          (optional)
209    * @return
210    */
211   @SuppressWarnings("unchecked")
212   protected Map<String, Object> getResult(String identifier, String objectType)
213   {
214     List<String> ids = Arrays.asList(new String[] { identifier });
215
216     try
217     {
218       return (Map<String, Object>) getJSON(getUrl(identifier, objectType), ids, -1, MODE_MAP, null);
219     } 
220     catch (IOException | ParseException e)
221     {
222       System.err.println("Error parsing " + identifier + " lookup response "
223               + e.getMessage());
224       return null;
225     }
226   }
227
228   /**
229    * Calls the /lookup/id rest service for the given id, and if successful,
230    * parses and returns the gene's chromosomal coordinates
231    * 
232    * @param geneId
233    * @return
234    */
235   public GeneLociI getGeneLoci(String geneId)
236   {
237     return parseGeneLoci(getResult(geneId, OBJECT_TYPE_GENE));
238   }
239
240   /**
241    * Parses the /lookup/id response for species, asssembly_name,
242    * seq_region_name, start, end and returns an object that wraps them, or null
243    * if unsuccessful
244    * 
245    * @param json
246    * @return
247    */
248   GeneLociI parseGeneLoci(Map<String, Object> json)
249   {
250     if (json == null)
251     {
252       return null;
253     }
254
255     try
256     {
257       final String species = json.get("species").toString();
258       final String assembly = json.get("assembly_name").toString();
259       final String chromosome = json.get("seq_region_name").toString();
260       String strand = json.get("strand").toString();
261       int start = Integer.parseInt(json.get("start").toString());
262       int end = Integer.parseInt(json.get("end").toString());
263       int fromEnd = end - start + 1;
264       boolean reverseStrand = "-1".equals(strand);
265       int toStart = reverseStrand ? end : start;
266       int toEnd = reverseStrand ? start : end;
267       List<int[]> fromRange = Collections.singletonList(new int[] { 1,
268           fromEnd });
269       List<int[]> toRange = Collections.singletonList(new int[] { toStart,
270           toEnd });
271       final MapList map = new MapList(fromRange, toRange, 1, 1);
272       return new GeneLociI()
273       {
274
275         @Override
276         public String getSpeciesId()
277         {
278           return species == null ? "" : species;
279         }
280
281         @Override
282         public String getAssemblyId()
283         {
284           return assembly;
285         }
286
287         @Override
288         public String getChromosomeId()
289         {
290           return chromosome;
291         }
292
293         @Override
294         public MapList getMap()
295         {
296           return map;
297         }
298       };
299     } catch (NullPointerException | NumberFormatException e)
300     {
301       Cache.log.error("Error looking up gene loci: " + e.getMessage());
302       e.printStackTrace();
303     }
304     return null;
305   }
306
307 }