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