Merge branch 'mmw/Release_2_12_ws_merge' into development/Release_2_12_Branch
[jalview.git] / src / jalview / ws2 / client / slivka / SlivkaWSDiscoverer.java
1 package jalview.ws2.client.slivka;
2
3 import java.io.IOException;
4 import java.net.MalformedURLException;
5 import java.net.URISyntaxException;
6 import java.net.URL;
7 import java.util.ArrayList;
8 import java.util.List;
9
10 import jalview.bin.Cache;
11 import jalview.bin.Console;
12 import jalview.ws.params.ParamManager;
13 import jalview.ws2.actions.alignment.AlignmentAction;
14 import jalview.ws2.actions.annotation.AnnotationAction;
15 import jalview.ws2.api.WebService;
16 import jalview.ws2.client.api.AbstractWebServiceDiscoverer;
17 import uk.ac.dundee.compbio.slivkaclient.SlivkaClient;
18 import uk.ac.dundee.compbio.slivkaclient.SlivkaService;
19
20 public class SlivkaWSDiscoverer extends AbstractWebServiceDiscoverer
21 {
22   private static final String SLIVKA_HOST_URLS = "SLIVKAHOSTURLS";
23
24   private static final URL DEFAULT_URL;
25   static
26   {
27     try
28     {
29       DEFAULT_URL = new URL("https://www.compbio.dundee.ac.uk/slivka/");
30     } catch (MalformedURLException e)
31     {
32       throw new AssertionError(e);
33     }
34   }
35
36   private static SlivkaWSDiscoverer instance = null;
37
38   private static ParamManager paramManager = null;
39
40   private SlivkaWSDiscoverer()
41   {
42   }
43
44   public static SlivkaWSDiscoverer getInstance()
45   {
46     if (instance == null)
47       instance = new SlivkaWSDiscoverer();
48     return instance;
49   }
50
51   public static void setParamManager(ParamManager manager)
52   {
53     paramManager = manager;
54   }
55
56   @Override
57   public int getStatusForUrl(URL url)
58   {
59     try
60     {
61       List<?> services = new SlivkaClient(url.toString()).getServices();
62       return services.isEmpty() ? STATUS_NO_SERVICES : STATUS_OK;
63     } catch (IOException e)
64     {
65       Console.error("slivka could not retrieve services from " + url, e);
66       return STATUS_INVALID;
67     }
68   }
69
70   @Override
71   protected String getUrlsPropertyKey()
72   {
73     return SLIVKA_HOST_URLS;
74   }
75
76   @Override
77   protected URL getDefaultUrl()
78   {
79     return DEFAULT_URL;
80   }
81
82   @Override
83   protected List<WebService<?>> fetchServices(URL url) throws IOException
84   {
85     ArrayList<WebService<?>> allServices = new ArrayList<>();
86     SlivkaClient slivkaClient;
87     try
88     {
89       slivkaClient = new SlivkaClient(url.toURI());
90     } catch (URISyntaxException e)
91     {
92       throw new MalformedURLException(e.getMessage());
93     }
94     for (var slivkaService : slivkaClient.getServices())
95     {
96       int serviceClass = getServiceClass(slivkaService);
97       if (serviceClass == SERVICE_CLASS_MSA)
98       {
99         var wsb = WebService.<AlignmentAction> newBuilder();
100         initServiceBuilder(slivkaService, wsb);
101         wsb.category("Alignment");
102         wsb.interactive(false);
103         wsb.actionClass(AlignmentAction.class);
104         var msaService = wsb.build();
105
106         boolean canRealign = msaService.getName().contains("lustal");
107         var client = new SlivkaAlignmentWSClient(slivkaService);
108         var actionBuilder = AlignmentAction.newBuilder(client);
109         actionBuilder.name("Alignment");
110         actionBuilder.webService(msaService);
111         if (canRealign)
112           actionBuilder.subcategory("Align");
113         actionBuilder.minSequences(2);
114         msaService.addAction(actionBuilder.build());
115         if (canRealign)
116         {
117           actionBuilder.name("Re-alignment");
118           actionBuilder.subcategory("Realign");
119           actionBuilder.submitGaps(true);
120           msaService.addAction(actionBuilder.build());
121         }
122         allServices.add(msaService);
123       }
124       else if (serviceClass == SERVICE_CLASS_PROT_SEQ_ANALYSIS)
125       {
126         var wsb = WebService.<AnnotationAction> newBuilder();
127         initServiceBuilder(slivkaService, wsb);
128         wsb.category("Protein Disorder");
129         wsb.interactive(false);
130         wsb.actionClass(AnnotationAction.class);
131         var psaService = wsb.build();
132         var client = new SlivkaAnnotationWSClient(slivkaService);
133         var actionBuilder = AnnotationAction.newBuilder(client);
134         actionBuilder.webService(psaService);
135         actionBuilder.name("Analysis");
136         psaService.addAction(actionBuilder.build());
137         allServices.add(psaService);
138       }
139       else if (serviceClass == SERVICE_CLASS_CONSERVATION)
140       {
141         var wsb = WebService.<AnnotationAction> newBuilder();
142         initServiceBuilder(slivkaService, wsb);
143         wsb.category("Conservation");
144         wsb.interactive(true);
145         wsb.actionClass(AnnotationAction.class);
146         var conService = wsb.build();
147         var client = new SlivkaAnnotationWSClient(slivkaService);
148         var actionBuilder = AnnotationAction.newBuilder(client);
149         actionBuilder.webService(conService);
150         actionBuilder.name("");
151         actionBuilder.alignmentAnalysis(true);
152         actionBuilder.requireAlignedSequences(true);
153         actionBuilder.filterSymbols(true);
154         conService.addAction(actionBuilder.build());
155         allServices.add(conService);
156       }
157       else if (serviceClass == SERVICE_CLASS_RNA_SEC_STR_PRED)
158       {
159         var wsb = WebService.<AnnotationAction> newBuilder();
160         initServiceBuilder(slivkaService, wsb);
161         wsb.category("Secondary Structure Prediction");
162         wsb.interactive(true);
163         wsb.actionClass(AnnotationAction.class);
164         var predService = wsb.build();
165         var client = new SlivkaAnnotationWSClient(slivkaService);
166         var actionBuilder = AnnotationAction.newBuilder(client);
167         actionBuilder.webService(predService);
168         actionBuilder.name("Prediction");
169         actionBuilder.minSequences(2);
170         actionBuilder.allowNucleotide(true);
171         actionBuilder.allowProtein(false);
172         actionBuilder.alignmentAnalysis(true);
173         actionBuilder.requireAlignedSequences(true);
174         actionBuilder.filterSymbols(false);
175         predService.addAction(actionBuilder.build());
176         allServices.add(predService);
177       }
178       else
179       {
180         continue;
181       }
182     }
183     return allServices;
184   }
185
186   private void initServiceBuilder(SlivkaService service, WebService.Builder<?> wsBuilder)
187   {
188     try
189     {
190       wsBuilder.url(service.getClient().getUrl().toURL());
191     } catch (MalformedURLException e)
192     {
193       e.printStackTrace();
194     }
195     wsBuilder.clientName("slivka");
196     wsBuilder.name(service.getName());
197     wsBuilder.description(service.getDescription());
198     var storeBuilder = new SlivkaParamStoreFactory(service, paramManager);
199     wsBuilder.paramDatastore(storeBuilder.createParamDatastore());
200   }
201
202   static final int SERVICE_CLASS_UNSUPPORTED = -1;
203
204   static final int SERVICE_CLASS_MSA = 1;
205
206   static final int SERVICE_CLASS_RNA_SEC_STR_PRED = 2;
207
208   static final int SERVICE_CLASS_CONSERVATION = 3;
209
210   static final int SERVICE_CLASS_PROT_SEQ_ANALYSIS = 4;
211
212   static final int SERVICE_CLASS_PROT_SEC_STR_PRED = 5;
213
214   /**
215    * Scan service classifiers starting with operation :: analysis to decide the
216    * operation class.
217    * 
218    * @return service class flag
219    */
220   private static int getServiceClass(SlivkaService service)
221   {
222     for (String classifier : service.getClassifiers())
223     {
224       String[] path = classifier.split("\\s*::\\s*");
225       if (path.length < 3 || !path[0].equalsIgnoreCase("operation") ||
226           !path[1].equalsIgnoreCase("analysis"))
227         continue;
228       // classifier is operation :: analysis :: *
229       var tail = path[path.length - 1].toLowerCase();
230       switch (tail)
231       {
232       case "multiple sequence alignment":
233         return SERVICE_CLASS_MSA;
234       case "rna secondary structure prediction":
235         return SERVICE_CLASS_RNA_SEC_STR_PRED;
236       case "sequence alignment analysis (conservation)":
237         return SERVICE_CLASS_CONSERVATION;
238       case "protein sequence analysis":
239         return SERVICE_CLASS_PROT_SEQ_ANALYSIS;
240       case "protein secondary structure prediction":
241         return SERVICE_CLASS_PROT_SEC_STR_PRED;
242       }
243     }
244     return SERVICE_CLASS_UNSUPPORTED;
245   }
246 }