Jalview-JS/JAL-3253-applet additional static final preferences
[jalview.git] / src / jalview / fts / service / uniprot / UniProtFTSRestClient.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
22 package jalview.fts.service.uniprot;
23
24 import jalview.bin.ApplicationSingletonProvider;
25 import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
26 import jalview.bin.Cache;
27 import jalview.fts.api.FTSData;
28 import jalview.fts.api.FTSDataColumnI;
29 import jalview.fts.api.FTSRestClientI;
30 import jalview.fts.core.FTSRestClient;
31 import jalview.fts.core.FTSRestRequest;
32 import jalview.fts.core.FTSRestResponse;
33 import jalview.util.MessageManager;
34 import jalview.util.Platform;
35
36 import java.util.ArrayList;
37 import java.util.Collection;
38 import java.util.List;
39 import java.util.Objects;
40
41 import javax.ws.rs.core.MediaType;
42
43 import com.sun.jersey.api.client.Client;
44 import com.sun.jersey.api.client.ClientResponse;
45 import com.sun.jersey.api.client.WebResource;
46 import com.sun.jersey.api.client.config.DefaultClientConfig;
47
48 public class UniProtFTSRestClient extends FTSRestClient
49         implements ApplicationSingletonI
50 {
51
52   public static FTSRestClientI getInstance()
53   {
54     return (FTSRestClientI) ApplicationSingletonProvider
55             .getInstance(UniProtFTSRestClient.class);
56   }
57
58   private UniProtFTSRestClient()
59   {
60     // singleton -- use getInstance()
61
62     uniprotSearchEndpoint = Cache.getDefault(Preferences.UNIPROT_DOMAIN,
63             DEFAULT_UNIPROT_DOMAIN) + "/uniprot/";    
64   }
65
66   private static final String DEFAULT_UNIPROT_DOMAIN = "https://www.uniprot.org";
67
68   public final String uniprotSearchEndpoint;
69
70   static
71   {
72     Platform.addJ2SDirectDatabaseCall(DEFAULT_UNIPROT_DOMAIN);
73   }
74   @SuppressWarnings("unchecked")
75   @Override
76   public FTSRestResponse executeRequest(FTSRestRequest uniprotRestRequest)
77           throws Exception
78   {
79     try
80     {
81       String wantedFields = getDataColumnsFieldsAsCommaDelimitedString(
82               uniprotRestRequest.getWantedFields());
83       int responseSize = (uniprotRestRequest.getResponseSize() == 0)
84               ? getDefaultResponsePageSize()
85               : uniprotRestRequest.getResponseSize();
86
87       int offSet = uniprotRestRequest.getOffSet();
88       String query;
89       if (isAdvancedQuery(uniprotRestRequest.getSearchTerm()))
90       {
91         query = uniprotRestRequest.getSearchTerm();
92       }
93       else
94       {
95         query = uniprotRestRequest.getFieldToSearchBy()
96                 .equalsIgnoreCase("Search All")
97                         ? uniprotRestRequest.getSearchTerm()
98                                 + " or mnemonic:"
99                                 + uniprotRestRequest.getSearchTerm()
100                         : uniprotRestRequest.getFieldToSearchBy() + ":"
101                                 + uniprotRestRequest.getSearchTerm();
102       }
103
104       // BH 2018 the trick here is to coerce the classes in Javascript to be
105       // different from the ones in Java yet still allow this to be correct for
106       // Java
107       Client client;
108       Class<ClientResponse> clientResponseClass;
109       if (Platform.isJS())
110       {
111         // JavaScript only -- coerce types to Java types for Java
112         client = (Client) (Object) new jalview.javascript.web.Client();
113         clientResponseClass = (Class<ClientResponse>) (Object) jalview.javascript.web.ClientResponse.class;
114       }
115       else
116       /**
117        * Java only
118        * 
119        * @j2sIgnore
120        */
121       {
122         // Java only
123         client = Client.create(new DefaultClientConfig());
124         clientResponseClass = ClientResponse.class;
125       }
126
127       WebResource webResource = null;
128       webResource = client.resource(uniprotSearchEndpoint)
129               .queryParam("format", "tab")
130               .queryParam("columns", wantedFields)
131               .queryParam("limit", String.valueOf(responseSize))
132               .queryParam("offset", String.valueOf(offSet))
133               .queryParam("sort", "score").queryParam("query", query);
134       // Execute the REST request
135       ClientResponse clientResponse = webResource
136               .accept(MediaType.TEXT_PLAIN).get(clientResponseClass);
137       String uniProtTabDelimittedResponseString = clientResponse
138               .getEntity(String.class);
139       // Make redundant objects eligible for garbage collection to conserve
140       // memory
141       // System.out.println(">>>>> response : "
142       // + uniProtTabDelimittedResponseString);
143       if (clientResponse.getStatus() != 200)
144       {
145         String errorMessage = getMessageByHTTPStatusCode(
146                 clientResponse.getStatus(), "Uniprot");
147         throw new Exception(errorMessage);
148
149       }
150       int xTotalResults = Platform.isJS() ? 1
151               : Integer.valueOf(clientResponse.getHeaders()
152                       .get("X-Total-Results").get(0));
153       clientResponse = null;
154       client = null;
155       return parseUniprotResponse(uniProtTabDelimittedResponseString,
156               uniprotRestRequest, xTotalResults);
157     } catch (Exception e)
158     {
159       String exceptionMsg = e.getMessage();
160       if (exceptionMsg.contains("SocketException"))
161       {
162         // No internet connection
163         throw new Exception(MessageManager.getString(
164                 "exception.unable_to_detect_internet_connection"));
165       }
166       else if (exceptionMsg.contains("UnknownHostException"))
167       {
168         // The server 'http://www.uniprot.org' is unreachable
169         throw new Exception(MessageManager.formatMessage(
170                 "exception.fts_server_unreachable", "Uniprot"));
171       }
172       else
173       {
174         throw e;
175       }
176     }
177   }
178
179   public boolean isAdvancedQuery(String query)
180   {
181     if (query.contains(" AND ") || query.contains(" OR ")
182             || query.contains(" NOT ") || query.contains(" ! ")
183             || query.contains(" || ") || query.contains(" && ")
184             || query.contains(":") || query.contains("-"))
185     {
186       return true;
187     }
188     return false;
189   }
190
191   public FTSRestResponse parseUniprotResponse(
192           String uniProtTabDelimittedResponseString,
193           FTSRestRequest uniprotRestRequest, int xTotalResults)
194   {
195     FTSRestResponse searchResult = new FTSRestResponse();
196     List<FTSData> result = null;
197     if (uniProtTabDelimittedResponseString == null
198             || uniProtTabDelimittedResponseString.trim().isEmpty())
199     {
200       searchResult.setNumberOfItemsFound(0);
201       return searchResult;
202     }
203     String[] foundDataRow = uniProtTabDelimittedResponseString.split("\n");
204     if (foundDataRow != null && foundDataRow.length > 0)
205     {
206       result = new ArrayList<>();
207       boolean firstRow = true;
208       for (String dataRow : foundDataRow)
209       {
210         // The first data row is usually the header data. This should be
211         // filtered out from the rest of the data See: JAL-2485
212         if (firstRow)
213         {
214           firstRow = false;
215           continue;
216         }
217         // System.out.println(dataRow);
218         result.add(getFTSData(dataRow, uniprotRestRequest));
219       }
220       searchResult.setNumberOfItemsFound(xTotalResults);
221       searchResult.setSearchSummary(result);
222     }
223     return searchResult;
224   }
225
226   // /**
227   // * Takes a collection of FTSDataColumnI and converts its 'code' values into
228   // a
229   // * tab delimited string.
230   // *
231   // * @param dataColumnFields
232   // * the collection of FTSDataColumnI to process
233   // * @return the generated comma delimited string from the supplied
234   // * FTSDataColumnI collection
235   // */
236   // private String getDataColumnsFieldsAsTabDelimitedString(
237   // Collection<FTSDataColumnI> dataColumnFields)
238   // {
239   // String result = "";
240   // if (dataColumnFields != null && !dataColumnFields.isEmpty())
241   // {
242   // StringBuilder returnedFields = new StringBuilder();
243   // for (FTSDataColumnI field : dataColumnFields)
244   // {
245   // if (field.getName().equalsIgnoreCase("Uniprot Id"))
246   // {
247   // returnedFields.append("\t").append("Entry");
248   // }
249   // else
250   // {
251   // returnedFields.append("\t").append(field.getName());
252   // }
253   // }
254   // returnedFields.deleteCharAt(0);
255   // result = returnedFields.toString();
256   // }
257   // return result;
258   // }
259
260   public static FTSData getFTSData(String tabDelimittedDataStr,
261           FTSRestRequest request)
262   {
263     String primaryKey = null;
264
265     Object[] summaryRowData;
266
267     Collection<FTSDataColumnI> diplayFields = request.getWantedFields();
268     int colCounter = 0;
269     summaryRowData = new Object[diplayFields.size()];
270     String[] columns = tabDelimittedDataStr.split("\t");
271     for (FTSDataColumnI field : diplayFields)
272     {
273       try
274       {
275         String fieldData = columns[colCounter];
276         if (field.isPrimaryKeyColumn())
277         {
278           primaryKey = fieldData;
279           summaryRowData[colCounter++] = primaryKey;
280         }
281         else if (fieldData == null || fieldData.isEmpty())
282         {
283           summaryRowData[colCounter++] = null;
284         }
285         else
286         {
287           try
288           {
289             summaryRowData[colCounter++] = (field.getDataType()
290                     .getDataTypeClass() == Integer.class)
291                             ? Integer.valueOf(fieldData.replace(",", ""))
292                             : (field.getDataType()
293                                     .getDataTypeClass() == Double.class)
294                                             ? Double.valueOf(fieldData)
295                                             : fieldData;
296           } catch (Exception e)
297           {
298             e.printStackTrace();
299             System.out.println("offending value:" + fieldData);
300           }
301         }
302       } catch (Exception e)
303       {
304         // e.printStackTrace();
305       }
306     }
307
308     final String primaryKey1 = primaryKey;
309
310     final Object[] summaryRowData1 = summaryRowData;
311     return new FTSData()
312     {
313       @Override
314       public Object[] getSummaryData()
315       {
316         return summaryRowData1;
317       }
318
319       @Override
320       public Object getPrimaryKey()
321       {
322         return primaryKey1;
323       }
324
325       /**
326        * Returns a string representation of this object;
327        */
328       @Override
329       public String toString()
330       {
331         StringBuilder summaryFieldValues = new StringBuilder();
332         for (Object summaryField : summaryRowData1)
333         {
334           summaryFieldValues.append(
335                   summaryField == null ? " " : summaryField.toString())
336                   .append("\t");
337         }
338         return summaryFieldValues.toString();
339       }
340
341       /**
342        * Returns hash code value for this object
343        */
344       @Override
345       public int hashCode()
346       {
347         return Objects.hash(primaryKey1, this.toString());
348       }
349
350       @Override
351       public boolean equals(Object that)
352       {
353         return this.toString().equals(that.toString());
354       }
355     };
356   }
357
358   @Override
359   public String getColumnDataConfigFileName()
360   {
361     return "/fts/uniprot_data_columns.txt";
362   }
363
364 }