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