JAL-4232 Disable menu items for RNA services for proteins.
[jalview.git] / src / jalview / ws2 / gui / WebServicesMenuManager.java
index 3cc2531..f9b8c02 100644 (file)
@@ -3,17 +3,14 @@ package jalview.ws2.gui;
 import java.awt.Color;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
-import java.lang.ref.WeakReference;
 import java.net.URL;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 import java.util.TreeMap;
 import java.util.concurrent.CompletionStage;
 
@@ -24,6 +21,8 @@ import javax.swing.JMenuItem;
 import javax.swing.ToolTipManager;
 import javax.swing.border.EmptyBorder;
 
+import jalview.bin.Console;
+import jalview.datamodel.AlignmentI;
 import jalview.gui.AlignFrame;
 import jalview.gui.Desktop;
 import jalview.gui.JvSwingUtils;
@@ -33,17 +32,20 @@ import jalview.viewmodel.AlignmentViewport;
 import jalview.ws.params.ArgumentI;
 import jalview.ws.params.ParamDatastoreI;
 import jalview.ws.params.WsParamSetI;
+import jalview.ws2.actions.BaseTask;
+import jalview.ws2.actions.PollingTaskExecutor;
 import jalview.ws2.actions.alignment.AlignmentAction;
+import jalview.ws2.actions.alignment.AlignmentResult;
+import jalview.ws2.actions.annotation.AlignCalcWorkerAdapter;
 import jalview.ws2.actions.annotation.AnnotationAction;
 import jalview.ws2.actions.api.ActionI;
+import jalview.ws2.actions.api.TaskEventListener;
 import jalview.ws2.actions.api.TaskI;
+import jalview.ws2.actions.hmmer.PhmmerAction;
 import jalview.ws2.api.Credentials;
 import jalview.ws2.api.WebService;
 import jalview.ws2.client.api.WebServiceProviderI;
 
-import static java.lang.String.format;
-import static java.util.Objects.requireNonNullElse;
-
 public class WebServicesMenuManager
 {
   private final JMenu menu;
@@ -59,8 +61,6 @@ public class WebServicesMenuManager
     noServicesItem.setEnabled(false);
   }
 
-  private Map<String, WeakReference<TaskI<?>>> interactiveTasks = new HashMap<>();
-
   public WebServicesMenuManager(String name, AlignFrame frame)
   {
     this.frame = frame;
@@ -121,9 +121,13 @@ public class WebServicesMenuManager
 
   private void addOneshotEntries(List<WebService<?>> services, JMenu menu)
   {
-    services.sort(Comparator
-        .<WebService<?>, String> comparing(s -> s.getUrl().toString())
-        .thenComparing(WebService::getName));
+    // Workaround. Comparator methods not working in j2s
+    services.sort((ws1, ws2) -> {
+      var res = ws1.getUrl().toString().compareTo(ws2.getUrl().toString());
+      if (res == 0)
+        res = ws1.getName().compareTo(ws2.getName());
+      return res;
+    });
     URL lastHost = null;
     for (WebService<?> service : services)
     {
@@ -146,28 +150,32 @@ public class WebServicesMenuManager
       {
         actionsByCategory
             .computeIfAbsent(
-                Objects.requireNonNullElse(action.getSubcategory(), ""),
+                action.getSubcategory() != null ? action.getSubcategory() : "",
                 k -> new ArrayList<>())
             .add(action);
       }
-      actionsByCategory.forEach((k, v) -> {
+      for (var entry : actionsByCategory.entrySet())
+      {
+        var category = entry.getKey();
+        var actions = entry.getValue();
         // create submenu named {subcategory} with {service} or use root menu
-        var atMenu = k.isEmpty() ? menu : new JMenu(String.format("%s with %s", k, service.getName()));
+        var atMenu = category.isEmpty() ? menu : new JMenu(String.format("%s with %s", category, service.getName()));
         if (atMenu != menu)
           menu.add(atMenu); // add only if submenu
         // sort actions by name pulling nulls to the front
-        v.sort(Comparator.comparing(
+        actions.sort(Comparator.comparing(
             ActionI::getName, Comparator.nullsFirst(Comparator.naturalOrder())));
-        for (ActionI<?> action : v)
+        for (int i = 0; i < actions.size(); i++)
         {
-          addEntriesForAction(action, atMenu, atMenu == menu);
+          addEntriesForAction(actions.get(i), atMenu, atMenu == menu);
         }
-      });
+      }
     }
   }
 
   private void addEntriesForAction(ActionI<?> action, JMenu menu, boolean isTopLevel)
   {
+    var enabled = isActionEnabled(action);
     var service = action.getWebService();
     String itemName;
     if (isTopLevel)
@@ -189,6 +197,7 @@ public class WebServicesMenuManager
       if (datastore.hasParameters() || datastore.hasPresets())
         text += " with defaults";
       JMenuItem item = new JMenuItem(text);
+      item.setEnabled(enabled);
       item.addActionListener(e -> {
         runAction(action, frame.getCurrentView(), Collections.emptyList(),
             Credentials.empty());
@@ -198,6 +207,7 @@ public class WebServicesMenuManager
     if (datastore.hasParameters())
     {
       JMenuItem item = new JMenuItem("Edit settings and run...");
+      item.setEnabled(enabled);
       item.addActionListener(e -> {
         openEditParamsDialog(datastore, null, null).thenAccept(args -> {
           if (args != null)
@@ -211,6 +221,7 @@ public class WebServicesMenuManager
     {
       final var presetsMenu = new JMenu(MessageManager.formatMessage(
           "label.run_with_preset_params", service.getName()));
+      presetsMenu.setEnabled(enabled);
       final int dismissDelay = ToolTipManager.sharedInstance()
           .getDismissDelay();
       final int QUICK_TOOLTIP = 1500;
@@ -293,9 +304,12 @@ public class WebServicesMenuManager
     private void buildAlternativesMenu(List<WebService<?>> services)
     {
       var menu = alternativesMenu;
-      services.sort(Comparator
-          .<WebService<?>, String> comparing(s -> s.getUrl().toString())
-          .thenComparing(s -> s.getName()));
+      services.sort((ws1, ws2) -> {
+        var res = ws1.getUrl().toString().compareTo(ws2.getUrl().toString());
+        if (res == 0)
+          res = ws1.getName().compareTo(ws2.getName());
+        return res;
+      });
       URL lastHost = null;
       for (var service : services)
       {
@@ -317,7 +331,7 @@ public class WebServicesMenuManager
         {
           actionsByCategory
               .computeIfAbsent(
-                  requireNonNullElse(action.getSubcategory(), ""),
+                  action.getSubcategory() != null ? action.getSubcategory() : "",
                   k -> new ArrayList<>())
               .add(action);
         }
@@ -332,6 +346,7 @@ public class WebServicesMenuManager
           for (ActionI<?> action : actions)
           {
             var item = new JMenuItem(action.getFullName());
+            item.setEnabled(isActionEnabled(action));
             item.addActionListener(e -> setAlternative(action));
             atMenu.add(item);
           }
@@ -359,15 +374,8 @@ public class WebServicesMenuManager
         serviceItem.removeActionListener(l);
       WebService<?> service = action.getWebService();
       serviceItem.addActionListener(e -> {
-        if (serviceItem.getState())
-        {
-          cancelAndRunInteractive(action, frame.getCurrentView(), arguments,
-              Credentials.empty());
-        }
-        else
-        {
-          cancelInteractive(service.getName());
-        }
+        runAction(action, frame.getCurrentView(), arguments,
+            Credentials.empty());
       });
       serviceItem.setSelected(true);
 
@@ -386,7 +394,7 @@ public class WebServicesMenuManager
                   lastPreset[0] = null;
                   arguments.clear();
                   arguments.addAll(args);
-                  cancelAndRunInteractive(action, frame.getCurrentView(),
+                  runAction(action, frame.getCurrentView(),
                       arguments, Credentials.empty());
                 }
               });
@@ -403,14 +411,14 @@ public class WebServicesMenuManager
           var item = new JMenuItem(preset.getName());
           item.addActionListener(e -> {
             lastPreset[0] = preset;
-            cancelAndRunInteractive(action, frame.getCurrentView(),
+            runAction(action, frame.getCurrentView(),
                 preset.getArguments(), Credentials.empty());
           });
           presetsMenu.add(item);
         }
       }
 
-      cancelAndRunInteractive(action, frame.getCurrentView(), arguments,
+      runAction(action, frame.getCurrentView(), arguments,
           Credentials.empty());
     }
 
@@ -425,24 +433,14 @@ public class WebServicesMenuManager
     }
   }
 
-  private void cancelInteractive(String wsName)
-  {
-    var taskRef = interactiveTasks.get(wsName);
-    if (taskRef != null && taskRef.get() != null)
-      taskRef.get().cancel();
-    interactiveTasks.put(wsName, null);
-  }
-
-  private void cancelAndRunInteractive(ActionI<?> action,
-      AlignmentViewport viewport, List<ArgumentI> args, Credentials credentials)
+  private boolean isActionEnabled(ActionI<?> action)
   {
-    var wsName = action.getWebService().getName();
-    cancelInteractive(wsName);
-    var task = runAction(action, viewport, args, credentials);
-    interactiveTasks.put(wsName, new WeakReference<>(task));
+    var isNa = frame.getViewport().getAlignment().isNucleotide();
+    return ((isNa && action.doAllowNucleotide()) ||
+        (!isNa && action.doAllowProtein()));
   }
 
-  private TaskI<?> runAction(ActionI<?> action, AlignmentViewport viewport,
+  private void runAction(ActionI<?> action, AlignmentViewport viewport,
       List<ArgumentI> args, Credentials credentials)
   {
     // casting and instance checks can be avoided with some effort,
@@ -452,16 +450,53 @@ public class WebServicesMenuManager
       // TODO: test if selection contains enough sequences
       var _action = (AlignmentAction) action;
       var handler = new AlignmentServiceGuiHandler(_action, frame);
-      return _action.perform(viewport, args, credentials, handler);
+      BaseTask<?, AlignmentResult> task = _action.createTask(viewport, args, credentials);
+      var executor = PollingTaskExecutor.fromPool(viewport.getServiceExecutor());
+      task.addTaskEventListener(handler);
+      var future = executor.submit(task);
+      task.setCancelAction(() -> { future.cancel(true); });
+      return;
     }
     if (action instanceof AnnotationAction)
     {
+      var calcManager = viewport.getCalcManager();
+
       var _action = (AnnotationAction) action;
+      var worker = new AlignCalcWorkerAdapter(viewport, frame.alignPanel,
+          _action, args, credentials);
       var handler = new AnnotationServiceGuiHandler(_action, frame);
-      return _action.perform(viewport, args, credentials, handler);
+      worker.setWorkerListener(handler);
+      for (var w : calcManager.getWorkers())
+      {
+        if (worker.getCalcName() != null && worker.getCalcName().equals(w.getCalcName()))
+        {
+          calcManager.cancelWorker(w);
+          calcManager.removeWorker(w);
+        }
+      }
+      if (action.getWebService().isInteractive())
+        calcManager.registerWorker(worker);
+      else
+        calcManager.startWorker(worker);
+      return;
+    }
+    if (action instanceof PhmmerAction)
+    {
+      var _action = (PhmmerAction) action;
+      var handler = new SearchServiceGuiHandler(_action, frame);
+      TaskI<AlignmentI> task = _action.createTask(viewport, args, credentials);
+      var executor = PollingTaskExecutor.fromPool(viewport.getServiceExecutor());
+      task.addTaskEventListener(handler);
+      _action.perform(viewport, args, credentials, handler);
+      return;
     }
-    throw new IllegalArgumentException(
-        String.format("Illegal action type %s", action.getClass().getName()));
+    Console.warn(String.format(
+            "No known handler for action type %s. All output will be discarded.",
+            action.getClass().getName()));
+    var task = action.createTask(viewport, args, credentials);
+    task.addTaskEventListener(TaskEventListener.nullListener());
+    PollingTaskExecutor.fromPool(viewport.getServiceExecutor())
+      .submit(task);
   }
 
   private static CompletionStage<List<ArgumentI>> openEditParamsDialog(
@@ -469,7 +504,7 @@ public class WebServicesMenuManager
   {
     final WsJobParameters jobParams;
     if (preset == null && arguments != null && arguments.size() > 0)
-      jobParams = new WsJobParameters(paramStore, preset, arguments);
+      jobParams = new WsJobParameters(paramStore, null, arguments);
     else
       jobParams = new WsJobParameters(paramStore, preset, null);
     if (preset != null)