Merge branch 'features/JAL-2885UniprotHttps' into releases/Release_2_10_4_Branch
[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.datamodel.AlignmentI;
24
25 import java.io.BufferedReader;
26 import java.io.IOException;
27 import java.net.MalformedURLException;
28 import java.net.URL;
29 import java.util.Arrays;
30 import java.util.List;
31
32 import org.json.simple.JSONObject;
33 import org.json.simple.parser.JSONParser;
34 import org.json.simple.parser.ParseException;
35
36 /**
37  * A client for the Ensembl lookup REST endpoint, used to find the gene
38  * identifier given a gene, transcript or protein identifier.
39  * 
40  * @author gmcarstairs
41  */
42 public class EnsemblLookup extends EnsemblRestClient
43 {
44
45   private static final String OBJECT_TYPE_TRANSLATION = "Translation";
46   private static final String PARENT = "Parent";
47   private static final String OBJECT_TYPE_TRANSCRIPT = "Transcript";
48   private static final String ID = "id";
49   private static final String OBJECT_TYPE_GENE = "Gene";
50   private static final String OBJECT_TYPE = "object_type";
51
52   /**
53    * keep track of last identifier retrieved to break loops
54    */
55   private String lastId;
56
57   /**
58    * Default constructor (to use rest.ensembl.org)
59    */
60   public EnsemblLookup()
61   {
62     super();
63   }
64
65   /**
66    * Constructor given the target domain to fetch data from
67    * 
68    * @param
69    */
70   public EnsemblLookup(String d)
71   {
72     super(d);
73   }
74
75   @Override
76   public String getDbName()
77   {
78     return "ENSEMBL";
79   }
80
81   @Override
82   public AlignmentI getSequenceRecords(String queries) throws Exception
83   {
84     return null;
85   }
86
87   @Override
88   protected URL getUrl(List<String> ids) throws MalformedURLException
89   {
90     String identifier = ids.get(0);
91     return getUrl(identifier, null);
92   }
93
94   /**
95    * Gets the url for lookup of the given identifier, optionally with objectType
96    * also specified in the request
97    * 
98    * @param identifier
99    * @param objectType
100    * @return
101    */
102   protected URL getUrl(String identifier, String objectType)
103   {
104     String url = getDomain() + "/lookup/id/" + identifier
105             + CONTENT_TYPE_JSON;
106     if (objectType != null)
107     {
108       url += "&" + OBJECT_TYPE + "=" + objectType;
109     }
110
111     try
112     {
113       return new URL(url);
114     } catch (MalformedURLException e)
115     {
116       return null;
117     }
118   }
119
120   @Override
121   protected boolean useGetRequest()
122   {
123     return true;
124   }
125
126   @Override
127   protected String getRequestMimeType(boolean multipleIds)
128   {
129     return "application/json";
130   }
131
132   @Override
133   protected String getResponseMimeType()
134   {
135     return "application/json";
136   }
137
138   /**
139    * Returns the gene id related to the given identifier, which may be for a
140    * gene, transcript or protein
141    * 
142    * @param identifier
143    * @return
144    */
145   public String getGeneId(String identifier)
146   {
147     return getGeneId(identifier, null);
148   }
149
150   /**
151    * Calls the Ensembl lookup REST endpoint and retrieves the 'Parent' for the
152    * given identifier, or null if not found
153    * 
154    * @param identifier
155    * @param objectType
156    *          (optional)
157    * @return
158    */
159   public String getGeneId(String identifier, String objectType)
160   {
161     List<String> ids = Arrays.asList(new String[] { identifier });
162
163     BufferedReader br = null;
164     try
165     {
166
167       URL url = getUrl(identifier, objectType);
168
169       if (identifier.equals(lastId))
170       {
171         System.err.println("** Ensembl lookup " + url.toString()
172                 + " looping on Parent!");
173         return null;
174       }
175
176       lastId = identifier;
177
178       if (url != null)
179       {
180         br = getHttpResponse(url, ids);
181       }
182       return br == null ? null : parseResponse(br);
183     } catch (IOException e)
184     {
185       // ignore
186       return null;
187     } finally
188     {
189       if (br != null)
190       {
191         try
192         {
193           br.close();
194         } catch (IOException e)
195         {
196           // ignore
197         }
198       }
199     }
200   }
201
202   /**
203    * Parses the JSON response and returns the gene identifier, or null if not
204    * found. If the returned object_type is Gene, returns the id, if Transcript
205    * returns the Parent. If it is Translation (peptide identifier), then the
206    * Parent is the transcript identifier, so we redo the search with this value.
207    * 
208    * @param br
209    * @return
210    * @throws IOException
211    */
212   protected String parseResponse(BufferedReader br) throws IOException
213   {
214     String geneId = null;
215     JSONParser jp = new JSONParser();
216     try
217     {
218       JSONObject val = (JSONObject) jp.parse(br);
219       String type = val.get(OBJECT_TYPE).toString();
220       if (OBJECT_TYPE_GENE.equalsIgnoreCase(type))
221       {
222         // got the gene - just returns its id
223         geneId = val.get(ID).toString();
224       }
225       else if (OBJECT_TYPE_TRANSCRIPT.equalsIgnoreCase(type))
226       {
227         // got the transcript - return its (Gene) Parent
228         geneId = val.get(PARENT).toString();
229       }
230       else if (OBJECT_TYPE_TRANSLATION.equalsIgnoreCase(type))
231       {
232         // got the protein - get its Parent, restricted to type Transcript
233         String transcriptId = val.get(PARENT).toString();
234         geneId = getGeneId(transcriptId, OBJECT_TYPE_TRANSCRIPT);
235       }
236     } catch (ParseException e)
237     {
238       // ignore
239     }
240     return geneId;
241   }
242
243 }