JAL-3878 Implement prot. seq. an. 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
139       {
140         continue;
141       }
142     }
143     return allServices;
144   }
145
146   private void initServiceBuilder(SlivkaService service, WebService.Builder<?> wsBuilder)
147   {
148     try
149     {
150       wsBuilder.url(service.getUrl().toURL());
151     } catch (MalformedURLException e)
152     {
153       e.printStackTrace();
154     }
155     wsBuilder.clientName("slivka");
156     wsBuilder.name(service.getName());
157     wsBuilder.description(service.getDescription());
158     var storeBuilder = new SlivkaParamStoreFactory(service, paramManager);
159     wsBuilder.paramDatastore(storeBuilder.createParamDatastore());
160   }
161
162   static final int SERVICE_CLASS_UNSUPPORTED = -1;
163
164   static final int SERVICE_CLASS_MSA = 1;
165
166   static final int SERVICE_CLASS_RNA_SEC_STR_PRED = 2;
167
168   static final int SERVICE_CLASS_CONSERVATION = 3;
169
170   static final int SERVICE_CLASS_PROT_SEQ_ANALYSIS = 4;
171
172   static final int SERVICE_CLASS_PROT_SEC_STR_PRED = 5;
173
174   /**
175    * Scan service classifiers starting with operation :: analysis to decide the
176    * operation class.
177    * 
178    * @return service class flag
179    */
180   private static int getServiceClass(SlivkaService service)
181   {
182     for (String classifier : service.getClassifiers())
183     {
184       String[] path = classifier.split("\\s*::\\s*");
185       if (path.length < 3 || !path[0].equalsIgnoreCase("operation") ||
186           !path[1].equalsIgnoreCase("analysis"))
187         continue;
188       // classifier is operation :: analysis :: *
189       var tail = path[path.length - 1].toLowerCase();
190       switch (tail)
191       {
192       case "multiple sequence alignment":
193         return SERVICE_CLASS_MSA;
194       case "rna secondary structure prediction":
195         return SERVICE_CLASS_RNA_SEC_STR_PRED;
196       case "sequence alignment analysis (conservation)":
197         return SERVICE_CLASS_CONSERVATION;
198       case "protein sequence analysis":
199         return SERVICE_CLASS_PROT_SEQ_ANALYSIS;
200       case "protein secondary structure prediction":
201         return SERVICE_CLASS_PROT_SEC_STR_PRED;
202       }
203     }
204     return SERVICE_CLASS_UNSUPPORTED;
205   }
206 }