JAL-3878 Create WebServiceMenuBuilder which will replace PreferredServiceRegistry
authorMateusz Warowny <mmzwarowny@dundee.ac.uk>
Wed, 20 Oct 2021 16:05:05 +0000 (18:05 +0200)
committerMateusz Warowny <mmzwarowny@dundee.ac.uk>
Wed, 20 Oct 2021 16:05:05 +0000 (18:05 +0200)
src/jalview/gui/AlignFrame.java
src/jalview/gui/WebServicesMenuBuilder.java [new file with mode: 0644]

index 9fb6ee5..3d00450 100644 (file)
@@ -4726,35 +4726,18 @@ public class AlignFrame extends GAlignFrame
     }
   }
 
-  private void buildWebServicesMenu(WebServiceDiscoverer discoverer, JMenu menu)
+  private void buildWebServicesMenu(WebServiceDiscoverer discoverer, final JMenu menu)
   {
     if (discoverer.hasServices())
     {
-      var services = new ArrayList<>(discoverer.getServices());
-      services.sort(Comparator
-              .<WebServiceI, String>comparing(o -> o.getHostName())
-              .<String>thenComparing(o -> o.getName()));
-      Map<String, String> lastHostForOperation = new HashMap<>();
-      for (final WebServiceI service : services)
-      {
-        var host = service.getHostName();
-        for (Operation op : service.getOperations())
-        {
-          JMenu atpoint = JvSwingUtils.findOrCreateMenu(menu, op.getTypeName());
-          String lastHost = lastHostForOperation.get(op.getTypeName());
-          if (lastHost != host) {
-            if (lastHost != null)
-              atpoint.addSeparator();
-            var menuItem = new JMenuItem(host);
-            menuItem.setForeground(Color.blue);
-            menuItem.addActionListener(e -> Desktop.showUrl(host));
-            atpoint.add(menuItem);
-            lastHostForOperation.put(op.getTypeName(), host);
-          }
-          atpoint.addSeparator();
-          op.getMenuBuilder().buildMenu(atpoint, this);
-        }
-      }
+      var builder = new WebServicesMenuBuilder();
+      for (var service : discoverer.getServices())
+        builder.addAllOperations(service.getOperations());
+      builder.addSelectedHostChangeListener((name, op) -> {
+        menu.removeAll();
+        builder.buildMenu(menu, this);
+      });
+      builder.buildMenu(menu, this);
     }
     if (discoverer.isRunning())
     {
diff --git a/src/jalview/gui/WebServicesMenuBuilder.java b/src/jalview/gui/WebServicesMenuBuilder.java
new file mode 100644 (file)
index 0000000..2a17bf4
--- /dev/null
@@ -0,0 +1,201 @@
+package jalview.gui;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+
+import jalview.gui.AlignFrame;
+import jalview.gui.Desktop;
+import jalview.util.MessageManager;
+import jalview.ws2.WebServiceI;
+import jalview.ws2.operations.Operation;
+
+public class WebServicesMenuBuilder
+{
+  @FunctionalInterface
+  public static interface SelectedHostChangeListener
+  {
+    public void selectedHostChanged(String opName, Operation op);
+  }
+
+  List<Operation> operations = new ArrayList<>();
+
+  /**
+   * Mapping of service name to the preferred url this service is hosted at.
+   */
+  Map<String, String> selectedHost = new HashMap<>();
+
+  public void addOperation(Operation op)
+  {
+    operations.add(op);
+  }
+
+  public void addAllOperations(Collection<Operation> operations)
+  {
+    this.operations.addAll(operations);
+  }
+
+  public void removeOperation(Operation op)
+  {
+    operations.remove(op);
+  }
+
+  public void clearOperations()
+  {
+    operations.clear();
+  }
+
+  public void buildMenu(JMenu atMenu, AlignFrame frame)
+  {
+    Map<String, List<Operation>> oneshotOperations = new HashMap<>();
+    Map<String, List<Operation>> interactiveOperations = new HashMap<>();
+    for (Operation op : operations)
+    {
+      var mapping = op.isInteractive() ? interactiveOperations : oneshotOperations;
+      if (!mapping.containsKey(op.getTypeName()))
+        mapping.put(op.getTypeName(), new ArrayList<>());
+      mapping.get(op.getTypeName()).add(op);
+    }
+    var keysSet = new HashSet<>(oneshotOperations.keySet());
+    keysSet.addAll(interactiveOperations.keySet());
+    var keys = new ArrayList<>(keysSet);
+    keys.sort(Comparator.<String>naturalOrder());
+    for (String opType : keys)
+    {
+      var submenu = new JMenu(opType);
+      var oneshot = oneshotOperations.get(opType);
+      if (oneshot != null)
+        addOneshotOperations(oneshot, submenu, frame);
+      var interactive = interactiveOperations.get(opType);
+      if (interactive != null)
+      {
+        if (oneshot != null)
+          submenu.addSeparator();
+        addInteractiveOperations(interactive, submenu, frame);
+      }
+      atMenu.add(submenu);
+    }
+  }
+
+  private void addOneshotOperations(List<Operation> operations, JMenu submenu,
+          AlignFrame frame)
+  {
+    operations = new ArrayList<>(operations);
+    operations.sort(Comparator
+            .<Operation, String>comparing(o -> o.getHostName())
+            .<String>thenComparing(o -> o.getName()));
+    String lastHost = null;
+    for (final Operation op : operations)
+    {
+      String host = op.getHostName();
+      if (lastHost != host)
+      {
+        if (lastHost != null)
+          submenu.addSeparator();
+        var menuItem = new JMenuItem(host);
+        menuItem.setForeground(Color.blue);
+        menuItem.addActionListener(e -> Desktop.showUrl(host));
+        submenu.add(menuItem);
+        lastHost = host;
+      }
+      submenu.addSeparator();
+      op.getMenuBuilder().buildMenu(submenu, frame);
+    }
+  }
+
+  private void addInteractiveOperations(List<Operation> operations,
+      JMenu submenu, AlignFrame frame)
+  {
+    Map<String, List<Operation>> groupedOperations = new HashMap<>();
+    for (Operation op : operations)
+    {
+      if (!groupedOperations.containsKey(op.getName()))
+        groupedOperations.put(op.getName(), new ArrayList<>());
+      groupedOperations.get(op.getName()).add(op);
+    }
+    var keys = new ArrayList<>(groupedOperations.keySet());
+    keys.sort(Comparator.<String>naturalOrder());
+    for (String opName : keys)
+    {
+      var ops = groupedOperations.get(opName);
+      var selectedHost = getSelectedHost(opName);
+      Operation selectedOperation = null;
+      for (var op : ops)
+      {
+        if (op.getHostName().equals(selectedHost))
+        {
+          selectedOperation = op;
+          break;
+        }
+      }
+      if (selectedOperation == null || selectedHost == null)
+        selectedOperation = ops.get(0);
+      {
+        final var hostName = selectedOperation.getHostName();
+        var hostItem = new JMenuItem(hostName);
+        hostItem.setForeground(Color.blue);
+        hostItem.addActionListener(e -> Desktop.showUrl(hostName));
+        submenu.add(hostItem);
+      }
+      selectedOperation.getMenuBuilder().buildMenu(submenu, frame);
+      if (ops.size() > 1)
+      {
+        JMenuItem alternatesMenu = new JMenuItem(MessageManager.getString("label.switch_server"));
+        submenu.add(alternatesMenu);
+        alternatesMenu.setToolTipText(JvSwingUtils.wrapTooltip(false,
+            MessageManager.getString("label.choose_jabaws_server")));
+        for (final Operation op : ops)
+        {
+          if (op == selectedOperation) continue;
+          var hostItem = new JMenuItem(op.getHostName());
+          hostItem.setForeground(Color.blue);
+          hostItem.addActionListener(e -> {
+            setSelectedHost(op.getName(), op.getHostName());
+            fireSelectedHostChanged(op.getName(), op);
+          });
+          submenu.add(hostItem);
+        }
+      }
+    }
+  }
+
+  public String getSelectedHost(String serviceName)
+  {
+    return selectedHost.getOrDefault(serviceName, null);
+  }
+
+  public void setSelectedHost(WebServiceI service)
+  {
+    setSelectedHost(service.getName(), service.getHostName());
+  }
+
+  public void setSelectedHost(String serviceName, String hostName)
+  {
+    selectedHost.put(serviceName, hostName);
+  }
+
+  List<SelectedHostChangeListener> hostChangeListeners = new CopyOnWriteArrayList<>();
+
+  public void addSelectedHostChangeListener(SelectedHostChangeListener l)
+  {
+    hostChangeListeners.add(l);
+  }
+
+  private void fireSelectedHostChanged(String opName, Operation op)
+  {
+    for (var listener : hostChangeListeners)
+    {
+      listener.selectedHostChanged(opName, op);
+    }
+  }
+
+}