b5a732894aa5e55324f917b8c63fd7bc748b4d97
[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 extension
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     try
206     {
207       URL rcall = new URL(url);
208       HttpURLConnection conn = (HttpURLConnection) rcall.openConnection();
209       int responseCode = conn.getResponseCode();
210       if (responseCode == 200)
211       {
212         is = conn.getInputStream();
213         if (outFile != null)
214         {
215           Platform.streamToFile(is, outFile);
216           return null;
217         }
218         BufferedReader br = new BufferedReader(new InputStreamReader(is));
219         String rtn;
220         List<String> arl = new ArrayList<>();
221         while ((rtn = br.readLine()) != null)
222         {
223           arl.add(rtn);
224         }
225         return (String[]) arl.toArray();
226       }
227       System.err.println(
228               "Warning: response code " + responseCode + " for " + url);
229     } catch (OutOfMemoryError er)
230     {
231       System.out.println("OUT OF MEMORY DOWNLOADING QUERY FROM " + database
232               + ":\n" + ids);
233       throw er;
234     } catch (Exception ex)
235     {
236       if (!ex.getMessage().startsWith(
237               "uk.ac.ebi.jdbfetch.exceptions.DbfNoEntryFoundException"))
238       {
239         System.err.println("Unexpected exception when retrieving from "
240                 + database + "\nQuery was : '" + ids + "'");
241         ex.printStackTrace(System.err);
242       }
243     } finally
244     {
245       if (is != null)
246       {
247         try
248         {
249           is.close();
250         } catch (IOException e)
251         {
252         }
253       }
254     }
255     return null;
256   }
257
258   /**
259    * Constructs the URL to fetch from
260    * 
261    * @param ids
262    * @param database
263    * @param format
264    * @return
265    */
266   static String buildUrl(String ids, String database, String format)
267   {
268     String url;
269     if (database.equalsIgnoreCase(DBRefSource.EMBL)
270             || database.equalsIgnoreCase(DBRefSource.EMBLCDS))
271     {
272       url = "https://www.ebi.ac.uk/ena/data/view/" + ids.toLowerCase()
273               + (format != null ? "&" + format : "");
274     }
275     else
276     {
277       url = "https://www.ebi.ac.uk/Tools/dbfetch/dbfetch/"
278               + database.toLowerCase() + "/" + ids.toLowerCase()
279               + (format != null ? "/" + format : "");
280     }
281     return url;
282   }
283 }