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