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