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