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