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