JAL-2113 EBIFetchClient refactor / unit tests / remove unused fields and
[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
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   String[] fetchData(String ids, String f, File outFile)
120           throws OutOfMemoryError
121   {
122     String[] rslts = new String[0];
123     StringBuilder querystring = new StringBuilder(ids.length());
124     String db = parseIds(ids, querystring);
125     if (db == null)
126     {
127       System.err.println("Invalid Query string : '" + ids + "'");
128       System.err.println("Should be of form 'dbname:q1;q2;q3;q4'");
129       return null;
130     }
131     String[] rslt = fetchBatch(querystring.toString(), db, f, outFile);
132     if (rslt != null)
133     {
134       String[] nrslts = new String[rslt.length + rslts.length];
135       System.arraycopy(rslts, 0, nrslts, 0, rslts.length);
136       System.arraycopy(rslt, 0, nrslts, rslts.length, rslt.length);
137       rslts = nrslts;
138     }
139
140     return (rslts.length == 0 ? null : rslts);
141   }
142
143   /**
144    * Parses ids formatted as dbname:q1;q2;q3, returns the dbname and adds
145    * queries as comma-separated items to the querystring. dbname must be
146    * specified for at least one queryId. Returns null if a mixture of different
147    * dbnames is found (ignoring case).
148    * 
149    * @param ids
150    * @param queryString
151    * @return
152    */
153   static String parseIds(String ids, StringBuilder queryString)
154   {
155     String database = null;
156     StringTokenizer queries = new StringTokenizer(ids, ";");
157     boolean appending = queryString.length() > 0;
158     while (queries.hasMoreTokens())
159     {
160       String query = queries.nextToken();
161       int p = query.indexOf(':');
162       if (p > -1)
163       {
164         String db = query.substring(0, p);
165         if (database != null && !db.equalsIgnoreCase(database))
166         {
167           /*
168            * different databases mixed in together - invalid
169            */
170           return null;
171         }
172         database = db;
173         query = query.substring(p + 1);
174       }
175       queryString.append(appending ? "," : "");
176       queryString.append(query);
177       appending = true;
178     }
179     return database;
180   }
181
182   String[] fetchBatch(String ids, String dbPath, String format,
183           File outFile) throws OutOfMemoryError
184   {
185     // long time = System.currentTimeMillis();
186     String url = buildUrl(ids, dbPath, format);
187
188     try
189     {
190       URL rcall = new URL(url);
191
192       InputStream is = new BufferedInputStream(rcall.openStream());
193       if (outFile != null)
194       {
195         FileOutputStream fio = new FileOutputStream(outFile);
196         byte[] bb = new byte[32 * 1024];
197         int l;
198         while ((l = is.read(bb)) > 0)
199         {
200           fio.write(bb, 0, l);
201         }
202         fio.close();
203         is.close();
204       }
205       else
206       {
207         BufferedReader br = new BufferedReader(new InputStreamReader(is));
208         String rtn;
209         List<String> arl = new ArrayList<String>();
210         while ((rtn = br.readLine()) != null)
211         {
212           arl.add(rtn);
213         }
214         return arl.toArray(new String[arl.size()]);
215       }
216     } catch (OutOfMemoryError er)
217     {
218
219       System.out.println("OUT OF MEMORY DOWNLOADING QUERY FROM " + dbPath
220               + ":\n" + ids);
221       throw er;
222     } catch (Exception ex)
223     {
224       if (ex.getMessage().startsWith(
225               "uk.ac.ebi.jdbfetch.exceptions.DbfNoEntryFoundException"))
226       {
227         return null;
228       }
229       System.err.println("Unexpected exception when retrieving from "
230               + dbPath
231               + "\nQuery was : '" + ids + "'");
232       ex.printStackTrace(System.err);
233       return null;
234     } finally
235     {
236       // System.err.println("EBIFetch took " + (System.currentTimeMillis() -
237       // time) + " ms");
238     }
239     return null;
240   }
241
242   /**
243    * Constructs the URL to fetch from
244    * 
245    * @param ids
246    * @param database
247    * @param format
248    * @return
249    */
250   static String buildUrl(String ids, String database, String format)
251   {
252     String url;
253     if (database.equalsIgnoreCase(DBRefSource.EMBL)
254             || database.equalsIgnoreCase(DBRefSource.EMBLCDS))
255     {
256       url = "http://www.ebi.ac.uk/ena/data/view/" + ids.toLowerCase()
257               + (format != null ? "&" + format : "");
258     }
259     else
260     {
261       url = "http://www.ebi.ac.uk/Tools/dbfetch/dbfetch/"
262               + database.toLowerCase() + "/" + ids.toLowerCase()
263               + (format != null ? "/" + format : "");
264     }
265     return url;
266   }
267 }