JAL-3692 EMBL flatfile fetcher / parser (todo: CDS dbrefs and mappings)
[jalview.git] / src / jalview / ws / ebi / EBIFetchClient.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.ws.ebi;
22
23 import jalview.datamodel.DBRefSource;
24 import jalview.util.MessageManager;
25 import jalview.util.Platform;
26
27 import java.io.BufferedReader;
28 import java.io.File;
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.io.InputStreamReader;
32 import java.net.HttpURLConnection;
33 import java.net.URL;
34 import java.util.ArrayList;
35 import java.util.List;
36 import java.util.StringTokenizer;
37
38 /**
39  * DOCUMENT ME!
40  * 
41  * @author $author$
42  * @version $Revision$
43  */
44 public class EBIFetchClient
45 {
46
47   /**
48    * Creates a new EBIFetchClient object.
49    */
50   public EBIFetchClient()
51   {
52   }
53
54   /**
55    * DOCUMENT ME!
56    * 
57    * @return DOCUMENT ME!
58    */
59   public String[] getSupportedDBs()
60   {
61     // TODO - implement rest call for dbfetch getSupportedDBs
62     throw new Error(MessageManager.getString("error.not_yet_implemented"));
63   }
64
65   /**
66    * DOCUMENT ME!
67    * 
68    * @return DOCUMENT ME!
69    */
70   public String[] getSupportedFormats()
71   {
72     // TODO - implement rest call for dbfetch getSupportedFormats
73     throw new Error(MessageManager.getString("error.not_yet_implemented"));
74   }
75
76   /**
77    * DOCUMENT ME!
78    * 
79    * @return DOCUMENT ME!
80    */
81   public String[] getSupportedStyles()
82   {
83     // TODO - implement rest call for dbfetch getSupportedStyles
84     throw new Error(MessageManager.getString("error.not_yet_implemented"));
85   }
86
87   /**
88    * Send an HTTP fetch request to EBI and save the reply in a temporary file.
89    * 
90    * @param ids
91    *          the query formatted as db:query1;query2;query3
92    * @param format
93    *          the format wanted
94    * @param ext
95    *          for the temporary file to hold response (without separator)
96    * @return the file holding the response
97    * @throws OutOfMemoryError
98    */
99
100   public File fetchDataAsFile(String ids, String format, String ext)
101           throws OutOfMemoryError
102   {
103     File outFile = null;
104     try
105     {
106       outFile = File.createTempFile("jalview", "." + ext);
107       outFile.deleteOnExit();
108       fetchData(ids, format, outFile);
109       if (outFile.length() == 0)
110       {
111         outFile.delete();
112         return null;
113       }
114     } catch (Exception ex)
115     {
116     }
117     return outFile;
118   }
119
120   /**
121    * Fetches queries and either saves the response to a file or returns as
122    * string data
123    * 
124    * @param ids
125    * @param format
126    * @param outFile
127    * @return
128    * @throws OutOfMemoryError
129    */
130   String[] fetchData(String ids, String format, File outFile)
131           throws OutOfMemoryError
132   {
133     StringBuilder querystring = new StringBuilder(ids.length());
134     String database = parseIds(ids, querystring);
135     if (database == null)
136     {
137       System.err.println("Invalid Query string : '" + ids + "'");
138       System.err.println("Should be of form 'dbname:q1;q2;q3;q4'");
139       return null;
140     }
141
142     // note: outFile is currently always specified, so return value is null
143     String[] rslt = fetchBatch(querystring.toString(), database, format,
144             outFile);
145
146     return (rslt != null && rslt.length > 0 ? rslt : null);
147   }
148
149   /**
150    * Parses ids formatted as dbname:q1;q2;q3, returns the dbname and adds
151    * queries as comma-separated items to the querystring. dbname must be
152    * specified for at least one queryId. Returns null if a mixture of different
153    * dbnames is found (ignoring case).
154    * 
155    * @param ids
156    * @param queryString
157    * @return
158    */
159   static String parseIds(String ids, StringBuilder queryString)
160   {
161     String database = null;
162     StringTokenizer queries = new StringTokenizer(ids, ";");
163     boolean appending = queryString.length() > 0;
164     while (queries.hasMoreTokens())
165     {
166       String query = queries.nextToken();
167       int p = query.indexOf(':');
168       if (p > -1)
169       {
170         String db = query.substring(0, p);
171         if (database != null && !db.equalsIgnoreCase(database))
172         {
173           /*
174            * different databases mixed in together - invalid
175            */
176           return null;
177         }
178         database = db;
179         query = query.substring(p + 1);
180       }
181       queryString.append(appending ? "," : "");
182       queryString.append(query);
183       appending = true;
184     }
185     return database;
186   }
187
188   /**
189    * Fetches queries and either saves the response to a file or (if no file
190    * specified) returns as string data
191    * 
192    * @param ids
193    * @param database
194    * @param format
195    * @param outFile
196    * @return array of lines from EBI only if outFile is null (which it will not
197    *         be)
198    * @throws OutOfMemoryError
199    */
200   String[] fetchBatch(String ids, String database, String format,
201           File outFile) throws OutOfMemoryError
202   {
203     String url = buildUrl(ids, database, format);
204     InputStream is = null;
205     BufferedReader br = null;
206     try
207     {
208       URL rcall = new URL(url);
209       HttpURLConnection conn = (HttpURLConnection) rcall.openConnection();
210       int responseCode = conn.getResponseCode();
211       if (responseCode == 200)
212       {
213         is = conn.getInputStream();
214         if (outFile != null)
215         {
216           Platform.streamToFile(is, outFile);
217           return null;
218         }
219         br = new BufferedReader(new InputStreamReader(is));
220         String rtn;
221         List<String> arl = new ArrayList<>();
222         while ((rtn = br.readLine()) != null)
223         {
224           arl.add(rtn);
225         }
226         return (String[]) arl.toArray();
227       }
228       System.err.println(
229               "Warning: response code " + responseCode + " for " + url);
230     } catch (OutOfMemoryError er)
231     {
232       System.out.println("OUT OF MEMORY DOWNLOADING QUERY FROM " + database
233               + ":\n" + ids);
234       throw er;
235     } catch (Exception ex)
236     {
237       if (!ex.getMessage().startsWith(
238               "uk.ac.ebi.jdbfetch.exceptions.DbfNoEntryFoundException"))
239       {
240         System.err.println("Unexpected exception when retrieving from "
241                 + database + "\nQuery was : '" + ids + "'");
242         ex.printStackTrace(System.err);
243       }
244     } finally
245     {
246       if (is != null)
247       {
248         try
249         {
250           is.close();
251         } catch (IOException e)
252         {
253         }
254       }
255       if (br != null)
256       {
257         try
258         {
259           br.close();
260         } catch (IOException e)
261         {
262         }
263       }
264     }
265     return null;
266   }
267
268   /**
269    * Constructs the URL to fetch from
270    * 
271    * @param ids
272    * @param database
273    * @param format
274    * @return
275    */
276   static String buildUrl(String ids, String database, String format)
277   {
278     String url;
279     if (database.equalsIgnoreCase(DBRefSource.EMBL)
280             || database.equalsIgnoreCase(DBRefSource.EMBLCDS))
281     {
282 //      url = "https://www.ebi.ac.uk/ena/data/view/" + ids.toLowerCase()
283 //              + (format != null ? "&" + format : "");
284       url = "https://www.ebi.ac.uk/ena/browser/api/embl/" + ids.toLowerCase();
285     }
286     else
287     {
288       url = "https://www.ebi.ac.uk/Tools/dbfetch/dbfetch/"
289               + database.toLowerCase() + "/" + ids.toLowerCase()
290               + (format != null ? "/" + format : "");
291     }
292     return url;
293   }
294 }