JAL-3829 allow multiple URL mocks and include P01308 in 3d-beacons mocks
[jalview.git] / src / jalview / fts / core / FTSRestClient.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 package jalview.fts.core;
22
23 import java.io.BufferedReader;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.InputStreamReader;
27 import java.util.ArrayList;
28 import java.util.Collection;
29 import java.util.HashMap;
30 import java.util.Objects;
31
32 import jalview.fts.api.FTSDataColumnI;
33 import jalview.fts.api.FTSDataColumnI.FTSDataColumnGroupI;
34 import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
35 import jalview.fts.service.threedbeacons.TDBeaconsFTSRestClient;
36 import jalview.fts.api.FTSRestClientI;
37
38 /**
39  * Base class providing implementation for common methods defined in
40  * FTSRestClientI
41  * 
42  * @author tcnofoegbu
43  * 
44  * @note implementations MUST be accessed as a singleton.
45  */
46 public abstract class FTSRestClient implements FTSRestClientI
47 {
48   protected Collection<FTSDataColumnI> dataColumns = new ArrayList<>();
49
50   protected Collection<FTSDataColumnGroupI> dataColumnGroups = new ArrayList<>();
51
52   protected Collection<FTSDataColumnI> searchableDataColumns = new ArrayList<>();
53
54   protected Collection<FTSDataColumnI> defaulDisplayedDataColumns = new ArrayList<>();
55
56   protected FTSDataColumnI primaryKeyColumn;
57
58   private String primaryKeyColumnCode = null;
59
60   private int defaultResponsePageSize = 100;
61
62   protected HashMap<String,String> mockQueries = null;
63
64   protected FTSRestClient()
65   {
66
67   }
68
69   public void parseDataColumnsConfigFile()
70   {
71     String fileName = getColumnDataConfigFileName();
72
73     InputStream in = getClass().getResourceAsStream(fileName);
74
75     try (BufferedReader br = new BufferedReader(new InputStreamReader(in))) 
76     {
77       String line;
78       while ((line = br.readLine()) != null)
79       {
80         final String[] lineData = line.split(";");
81         try
82         {
83           if (lineData.length == 2)
84           {
85             if (lineData[0].equalsIgnoreCase("_data_column.primary_key"))
86             {
87               primaryKeyColumnCode = lineData[1];
88             }
89             if (lineData[0].equalsIgnoreCase(
90                     "_data_column.default_response_page_size"))
91             {
92               defaultResponsePageSize = Integer.valueOf(lineData[1]);
93             }
94           }
95           else if (lineData.length == 3)
96           {
97             dataColumnGroups.add(new FTSDataColumnGroupI()
98             {
99               @Override
100               public String getID()
101               {
102                 return lineData[0];
103               }
104
105               @Override
106               public String getName()
107               {
108                 return lineData[1];
109               }
110
111               @Override
112               public int getSortOrder()
113               {
114                 return Integer.valueOf(lineData[2]);
115               }
116
117               @Override
118               public String toString()
119               {
120                 return lineData[1];
121               }
122
123               @Override
124               public int hashCode()
125               {
126                 return Objects.hash(this.getID(), this.getName(),
127                         this.getSortOrder());
128               }
129
130               @Override
131               public boolean equals(Object otherObject)
132               {
133                 FTSDataColumnGroupI that = (FTSDataColumnGroupI) otherObject;
134                 return this.getID().equals(that.getID())
135                         && this.getName().equals(that.getName())
136                         && this.getSortOrder() == that.getSortOrder();
137               }
138             });
139           }
140           else if (lineData.length > 6)
141           {
142             FTSDataColumnI dataCol = new FTSDataColumnI()
143             {
144               @Override
145               public String toString()
146               {
147                 return lineData[0];
148               }
149
150               @Override
151               public String getName()
152               {
153                 return lineData[0];
154               }
155
156               @Override
157               public String getCode()
158               {
159                 return lineData[1].split("\\|")[0];
160               }
161
162               @Override
163               public String getAltCode()
164               {
165                 return lineData[1].split("\\|").length > 1
166                         ? lineData[1].split("\\|")[1]
167                         : getCode();
168               }
169
170               @Override
171               public DataTypeI getDataType()
172               {
173                 final String[] dataTypeString = lineData[2].split("\\|");
174                 final String classString = dataTypeString[0].toUpperCase();
175
176                 return new DataTypeI()
177                 {
178
179                   @Override
180                   public boolean isFormtted()
181                   {
182                     if (dataTypeString.length > 1
183                             && dataTypeString[1] != null)
184                     {
185                       switch (dataTypeString[1].toUpperCase())
186                       {
187                       case "T":
188                       case "TRUE":
189                         return true;
190                       case "F":
191                       case "False":
192                       default:
193                         return false;
194                       }
195                     }
196                     return false;
197                   }
198
199                   @Override
200                   public int getSignificantFigures()
201                   {
202                     if (dataTypeString.length > 2
203                             && dataTypeString[2] != null)
204                     {
205                       return Integer.valueOf(dataTypeString[2]);
206                     }
207                     return 0;
208                   }
209
210                   @Override
211                   public Class getDataTypeClass()
212                   {
213                     switch (classString)
214                     {
215                     case "INT":
216                     case "INTEGER":
217                       return Integer.class;
218                     case "DOUBLE":
219                       return Double.class;
220                     case "STRING":
221                     default:
222                       return String.class;
223                     }
224                   }
225                 };
226
227               }
228
229               @Override
230               public FTSDataColumnGroupI getGroup()
231               {
232                 FTSDataColumnGroupI group = null;
233                 try
234                 {
235                   group = getDataColumnGroupById(lineData[3]);
236                 } catch (Exception e)
237                 {
238                   e.printStackTrace();
239                 }
240                 return group;
241               }
242
243               @Override
244               public int getMinWidth()
245               {
246                 return Integer.valueOf(lineData[4]);
247               }
248
249               @Override
250               public int getMaxWidth()
251               {
252                 return Integer.valueOf(lineData[5]);
253               }
254
255               @Override
256               public int getPreferredWidth()
257               {
258                 return Integer.valueOf(lineData[6]);
259               }
260
261               @Override
262               public boolean isPrimaryKeyColumn()
263               {
264                 return getName().equalsIgnoreCase(primaryKeyColumnCode)
265                         || getCode().equalsIgnoreCase(primaryKeyColumnCode);
266               }
267
268               @Override
269               public boolean isVisibleByDefault()
270               {
271                 return Boolean.valueOf(lineData[7]);
272               }
273
274               @Override
275               public boolean isSearchable()
276               {
277                 return Boolean.valueOf(lineData[8]);
278               }
279
280               @Override
281               public int hashCode()
282               {
283                 return Objects.hash(this.getName(), this.getCode(),
284                         this.getGroup());
285               }
286
287               @Override
288               public boolean equals(Object otherObject)
289               {
290                 FTSDataColumnI that = (FTSDataColumnI) otherObject;
291                 return otherObject == null ? false
292                         : this.getCode().equals(that.getCode())
293                         && this.getName().equals(that.getName())
294                         && this.getGroup().equals(that.getGroup());
295               }
296
297             };
298             dataColumns.add(dataCol);
299
300             if (dataCol.isSearchable())
301             {
302               searchableDataColumns.add(dataCol);
303             }
304
305             if (dataCol.isVisibleByDefault())
306             {
307               defaulDisplayedDataColumns.add(dataCol);
308             }
309
310           }
311           else
312           {
313             continue;
314           }
315         } catch (Exception e)
316         {
317           e.printStackTrace();
318         }
319       }
320       try
321       {
322         this.primaryKeyColumn = getDataColumnByNameOrCode(
323                 primaryKeyColumnCode);
324       } catch (Exception e)
325       {
326         e.printStackTrace();
327       }
328     } catch (IOException e)
329     {
330       e.printStackTrace();
331     }
332   }
333
334   @Override
335   public int getPrimaryKeyColumIndex(
336           Collection<FTSDataColumnI> wantedFields, boolean hasRefSeq)
337           throws Exception
338   {
339
340     // If a reference sequence is attached then start counting from 1 else
341     // start from zero
342     int pdbFieldIndexCounter = hasRefSeq ? 1 : 0;
343
344     for (FTSDataColumnI field : wantedFields)
345     {
346       if (field.isPrimaryKeyColumn())
347       {
348         break; // Once PDB Id index is determined exit iteration
349       }
350       ++pdbFieldIndexCounter;
351     }
352     return pdbFieldIndexCounter;
353   }
354
355   @Override
356   public String getDataColumnsFieldsAsCommaDelimitedString(
357           Collection<FTSDataColumnI> dataColumnFields)
358   {
359     String result = "";
360     if (dataColumnFields != null && !dataColumnFields.isEmpty())
361     {
362       StringBuilder returnedFields = new StringBuilder();
363       for (FTSDataColumnI field : dataColumnFields)
364       {
365         returnedFields.append(",").append(field.getCode());
366       }
367       returnedFields.deleteCharAt(0);
368       result = returnedFields.toString();
369     }
370     return result;
371   }
372
373   @Override
374   public Collection<FTSDataColumnI> getAllFTSDataColumns()
375   {
376     if (dataColumns == null || dataColumns.isEmpty())
377     {
378       parseDataColumnsConfigFile();
379     }
380     return dataColumns;
381   }
382
383   @Override
384   public Collection<FTSDataColumnI> getSearchableDataColumns()
385   {
386     if (searchableDataColumns == null || searchableDataColumns.isEmpty())
387     {
388       parseDataColumnsConfigFile();
389     }
390     return searchableDataColumns;
391   }
392
393   @Override
394   public Collection<FTSDataColumnI> getAllDefaultDisplayedFTSDataColumns()
395   {
396     if (defaulDisplayedDataColumns == null
397             || defaulDisplayedDataColumns.isEmpty())
398     {
399       parseDataColumnsConfigFile();
400     }
401     return defaulDisplayedDataColumns;
402   }
403
404   @Override
405   public FTSDataColumnI getPrimaryKeyColumn()
406   {
407     if (defaulDisplayedDataColumns == null
408             || defaulDisplayedDataColumns.isEmpty())
409     {
410       parseDataColumnsConfigFile();
411     }
412     return primaryKeyColumn;
413   }
414
415   @Override
416   public FTSDataColumnI getDataColumnByNameOrCode(String nameOrCode)
417           throws Exception
418   {
419     if (dataColumns == null || dataColumns.isEmpty())
420     {
421       parseDataColumnsConfigFile();
422     }
423     for (FTSDataColumnI column : dataColumns)
424     {
425       if (column.getName().equalsIgnoreCase(nameOrCode)
426               || column.getCode().equalsIgnoreCase(nameOrCode))
427       {
428         return column;
429       }
430     }
431     throw new Exception(
432             "Couldn't find data column with name : " + nameOrCode);
433   }
434
435   /**
436    * 
437    * @param instance
438    * @param mocks {{working query, working response}, ...}
439    */
440   public static void createMockFTSRestClient(FTSRestClient instance,String[][] mocks)
441   {
442     instance.setMock(mocks);
443   }
444
445   @Override
446   public FTSDataColumnGroupI getDataColumnGroupById(String id)
447           throws Exception
448   {
449     if (dataColumns == null || dataColumns.isEmpty())
450     {
451       parseDataColumnsConfigFile();
452     }
453     for (FTSDataColumnGroupI columnGroup : dataColumnGroups)
454     {
455       if (columnGroup.getID().equalsIgnoreCase(id))
456       {
457         return columnGroup;
458       }
459     }
460     throw new Exception("Couldn't find data column group with id : " + id);
461   }
462
463   public static String getMessageByHTTPStatusCode(int code, String service)
464   {
465     String message = "";
466     switch (code)
467     {
468     case 400:
469       message = "Bad request. There is a problem with your input.";
470       break;
471
472     case 410:
473       message = service + " rest services no longer available!";
474       break;
475     case 403:
476     case 404:
477       message = "The requested resource could not be found";
478       break;
479     case 408:
480     case 409:
481     case 500:
482     case 501:
483     case 502:
484     case 504:
485     case 505:
486       message = "There seems to be an error from the " + service
487               + " server";
488       break;
489     case 503:
490       message = "Service not available. The server is being updated, try again later.";
491       break;
492     default:
493       break;
494     }
495     return String.valueOf(code) + " " + message;
496   }
497
498   public static void unMock(FTSRestClient instance)
499   {
500     instance.mockQueries=null;
501   }
502
503   protected String getResourceFile(String fileName)
504   {
505     String result = "";
506     try
507     {
508       result = getClass().getResource(fileName).getFile();
509     } catch (Exception e)
510     {
511       e.printStackTrace();
512     }
513     return result;
514
515   }
516
517   @Override
518   public int getDefaultResponsePageSize()
519   {
520     if (dataColumns == null || dataColumns.isEmpty())
521     {
522       parseDataColumnsConfigFile();
523     }
524     return defaultResponsePageSize;
525   }
526
527   protected void setMock(String[][] mocks)
528   {
529     if (mocks==null) {
530       mockQueries=null;
531       return;
532     }
533     mockQueries=new HashMap<String,String>();
534     for (String[] mock:mocks)
535     {
536       mockQueries.put(mock[0],mock[1]);
537     }
538   }
539
540   protected boolean isMocked()
541   {
542     return mockQueries!=null;
543   }
544
545   @Override
546   public String[] getPreferencesColumnsFor(PreferenceSource source)
547   {
548     String[] columnNames = null;
549     switch (source)
550     {
551     case SEARCH_SUMMARY:
552       columnNames = new String[] { "", "Display", "Group" };
553       break;
554     default:
555       // non structure sources don't return any other kind of preferences columns
556       break;
557     }
558     return columnNames;
559   }
560 }