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