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