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