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