Merge branch 'alpha/JAL-3066_Jalview_212_slivka-integration' into alpha/JAL-3362_Jalv...
[jalview.git] / src / jalview / ws / jws2 / PreferredServiceRegistry.java
diff --git a/src/jalview/ws/jws2/PreferredServiceRegistry.java b/src/jalview/ws/jws2/PreferredServiceRegistry.java
new file mode 100644 (file)
index 0000000..9e7438c
--- /dev/null
@@ -0,0 +1,396 @@
+package jalview.ws.jws2;
+
+import jalview.bin.Cache;
+import jalview.gui.AlignFrame;
+import jalview.gui.Desktop;
+import jalview.gui.JvSwingUtils;
+import jalview.util.MessageManager;
+import jalview.ws.api.ServiceWithParameters;
+
+import java.awt.Color;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeSupport;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+
+public class PreferredServiceRegistry
+{
+
+  private static PreferredServiceRegistry us = new PreferredServiceRegistry();
+
+  public static PreferredServiceRegistry getRegistry()
+  {
+    if (us == null)
+    {
+      us = new PreferredServiceRegistry();
+    }
+    return us;
+  }
+
+  List<ServiceWithParameters> ourServices = new ArrayList();
+
+  /**
+   * forget about any known services
+   */
+  public void clearServices()
+  {
+    ourServices.clear();
+  }
+
+  public void populateWSMenuEntry(List<ServiceWithParameters> services,
+          PropertyChangeSupport changeSupport,
+          JMenu jws2al, final AlignFrame alignFrame, String typeFilter)
+  {
+    /**
+     * eventually, JWS2 services will appear under the same align/etc submenus.
+     * for moment we keep them separate.
+     */
+    JMenu atpoint;
+    List<ServiceWithParameters> enumerableServices = new ArrayList<>();
+    // jws2al.removeAll();
+    Map<String, ServiceWithParameters> preferredHosts = new HashMap<>();
+    Map<String, List<ServiceWithParameters>> alternates = new HashMap<>();
+    for (ServiceWithParameters service : services)
+    {
+      ourServices.add(service);
+      // TODO: check this behaves with refactored serviceType to getName
+      if (!service.isInteractiveUpdate())
+      {
+        // add 'one shot' services to be displayed using the classic menu
+        // structure
+        enumerableServices.add(service);
+      }
+      else
+      {
+        if (!preferredHosts.containsKey(service.getName()))
+        {
+          ServiceWithParameters preferredInstance = getPreferredServiceFor(
+                  alignFrame, service.getName());
+          if (preferredInstance != null)
+          {
+            preferredHosts.put(service.getName(), preferredInstance);
+          }
+          else
+          {
+            preferredHosts.put(service.getName(), service);
+          }
+        }
+        List<ServiceWithParameters> ph = alternates.get(service.getName());
+        if (preferredHosts.get(service.getName()) != service)
+        {
+          if (ph == null)
+          {
+            ph = new ArrayList<>();
+          }
+          ph.add(service);
+          alternates.put(service.getName(), ph);
+        }
+      }
+
+    }
+
+    // create GUI element for classic services
+    addEnumeratedServices(jws2al, alignFrame, enumerableServices);
+    // and the instantaneous services
+    for (final ServiceWithParameters service : preferredHosts.values())
+    {
+      atpoint = JvSwingUtils.findOrCreateMenu(jws2al,
+              service.getServiceType());
+      JMenuItem hitm;
+      if (atpoint.getItemCount() > 1)
+      {
+        // previous service of this type already present
+        atpoint.addSeparator();
+      }
+      atpoint.add(hitm = new JMenuItem(service.getHostURL()));
+      hitm.setForeground(Color.blue);
+      hitm.addActionListener(new ActionListener()
+      {
+
+        @Override
+        public void actionPerformed(ActionEvent e)
+        {
+          Desktop.showUrl(service.getHostURL());
+        }
+      });
+      hitm.setToolTipText(JvSwingUtils.wrapTooltip(false,
+              MessageManager.getString("label.open_jabaws_web_page")));
+
+      service.attachWSMenuEntry(atpoint, alignFrame);
+      if (alternates.containsKey(service.getName()))
+      {
+        atpoint.add(hitm = new JMenu(
+                MessageManager.getString("label.switch_server")));
+        hitm.setToolTipText(JvSwingUtils.wrapTooltip(false,
+                MessageManager.getString("label.choose_jabaws_server")));
+        for (final ServiceWithParameters sv : alternates
+                .get(service.getName()))
+        {
+          JMenuItem itm;
+          hitm.add(itm = new JMenuItem(sv.getHostURL()));
+          itm.setForeground(Color.blue);
+          itm.addActionListener(new ActionListener()
+          {
+
+            @Override
+            public void actionPerformed(ActionEvent arg0)
+            {
+              new Thread(new Runnable()
+              {
+                @Override
+                public void run()
+                {
+                  setPreferredServiceFor(alignFrame, sv.getName(),
+                          sv.getServiceType(), sv);
+                  changeSupport.firePropertyChange("services",
+                          new Vector<ServiceWithParameters>(), services);
+                };
+              }).start();
+
+            }
+          });
+        }
+      }
+    }
+  }
+
+  /**
+   * add services using the Java 2.5/2.6/2.7 system which optionally creates
+   * submenus to index by host and service program type
+   */
+  private void addEnumeratedServices(final JMenu jws2al,
+          final AlignFrame alignFrame,
+          List<ServiceWithParameters> enumerableServices)
+  {
+    boolean byhost = Cache.getDefault("WSMENU_BYHOST", false),
+            bytype = Cache.getDefault("WSMENU_BYTYPE", false);
+    /**
+     * eventually, JWS2 services will appear under the same align/etc submenus.
+     * for moment we keep them separate.
+     */
+    JMenu atpoint;
+
+    List<String> hostLabels = new ArrayList<>();
+    Hashtable<String, String> lasthostFor = new Hashtable<>();
+    Hashtable<String, ArrayList<ServiceWithParameters>> hosts = new Hashtable<>();
+    ArrayList<String> hostlist = new ArrayList<>();
+    for (ServiceWithParameters service : enumerableServices)
+    {
+      ArrayList<ServiceWithParameters> hostservices = hosts
+              .get(service.getHostURL());
+      if (hostservices == null)
+      {
+        hosts.put(service.getHostURL(), hostservices = new ArrayList<>());
+        hostlist.add(service.getHostURL());
+      }
+      hostservices.add(service);
+    }
+    // now add hosts in order of the given array
+    for (String host : hostlist)
+    {
+      ServiceWithParameters orderedsvcs[] = hosts.get(host)
+              .toArray(new ServiceWithParameters[1]);
+      String sortbytype[] = new String[orderedsvcs.length];
+      for (int i = 0; i < sortbytype.length; i++)
+      {
+        sortbytype[i] = orderedsvcs[i].getName();
+      }
+      jalview.util.QuickSort.sort(sortbytype, orderedsvcs);
+      for (final ServiceWithParameters service : orderedsvcs)
+      {
+        atpoint = JvSwingUtils.findOrCreateMenu(jws2al,
+                service.getAction());
+        String type = service.getName();
+        if (byhost)
+        {
+          atpoint = JvSwingUtils.findOrCreateMenu(atpoint, host);
+          if (atpoint.getToolTipText() == null)
+          {
+            atpoint.setToolTipText(MessageManager
+                    .formatMessage("label.services_at", new String[]
+                    { host }));
+          }
+        }
+        if (bytype)
+        {
+          atpoint = JvSwingUtils.findOrCreateMenu(atpoint, type);
+          if (atpoint.getToolTipText() == null)
+          {
+            atpoint.setToolTipText(service.getActionText());
+          }
+        }
+        if (!byhost && !hostLabels.contains(
+                host + service.getName() + service.getActionText()))
+        // !hostLabels.contains(host + (bytype ?
+        // service.serviceType+service.getActionText() : "")))
+        {
+          // add a marker indicating where this service is hosted
+          // relies on services from the same host being listed in a
+          // contiguous
+          // group
+          JMenuItem hitm;
+          if (hostLabels.contains(host))
+          {
+            atpoint.addSeparator();
+          }
+          else
+          {
+            hostLabels.add(host);
+          }
+          if (lasthostFor.get(service.getAction()) == null
+                  || !lasthostFor.get(service.getAction()).equals(host))
+          {
+            atpoint.add(hitm = new JMenuItem(host));
+            hitm.setForeground(Color.blue);
+            hitm.addActionListener(new ActionListener()
+            {
+
+              @Override
+              public void actionPerformed(ActionEvent e)
+              {
+                Desktop.showUrl(service.getHostURL());
+              }
+            });
+            hitm.setToolTipText(
+                    JvSwingUtils.wrapTooltip(true, MessageManager
+                            .getString("label.open_jabaws_web_page")));
+            lasthostFor.put(service.getAction(), host);
+          }
+          hostLabels
+                  .add(host + service.getName() + service.getActionText());
+        }
+
+        service.attachWSMenuEntry(atpoint, alignFrame);
+      }
+    }
+  }
+
+  /**
+   * pick the user's preferred service based on a set of URLs (jaba server
+   * locations) and service URIs (specifying version and service interface
+   * class)
+   * 
+   * @param serviceURL
+   * @return null or best match for given uri/ls.
+   */
+  public ServiceWithParameters getPreferredServiceFor(String[] serviceURLs)
+  {
+    HashSet<String> urls = new HashSet<>();
+    urls.addAll(Arrays.asList(serviceURLs));
+    ServiceWithParameters match = null;
+
+    if (ourServices != null)
+    {
+      for (ServiceWithParameters svc : ourServices)
+      {
+        // TODO getNameURI Should return a versioned URI for the service, but
+        // doesn't as of 2.11
+        if (urls.contains(svc.getNameURI()))
+        {
+          if (match == null)
+          {
+            // for moment we always pick service from server ordered first in
+            // user's preferences
+            match = svc;
+          }
+          if (urls.contains(svc.getUri()))
+          {
+            // stop and return - we've matched type URI and URI for service
+            // endpoint
+            return svc;
+          }
+        }
+      }
+    }
+    return match;
+  }
+
+  Map<String, Map<String, String>> preferredServiceMap = new HashMap<>();
+
+  /**
+   * get current preferred endpoint of the given Jabaws service, or global
+   * default
+   * 
+   * @param af
+   *          null or a specific alignFrame
+   * @param serviceName
+   *          ServiceWithParameters.getName() for service
+   * @return null if no service of this type is available, the preferred service
+   *         for the serviceType and af if specified and if defined.
+   */
+  public ServiceWithParameters getPreferredServiceFor(AlignFrame af,
+          String serviceName)
+  {
+    String serviceurl = null;
+    synchronized (preferredServiceMap)
+    {
+      String afid = (af == null) ? "" : af.getViewport().getSequenceSetId();
+      Map<String, String> prefmap = preferredServiceMap.get(afid);
+      if (afid.length() > 0 && prefmap == null)
+      {
+        // recover global setting, if any
+        prefmap = preferredServiceMap.get("");
+      }
+      if (prefmap != null)
+      {
+        serviceurl = prefmap.get(serviceName);
+      }
+
+    }
+    ServiceWithParameters response = null;
+    for (ServiceWithParameters svc : ourServices)
+    {
+      if (svc.getName().equals(serviceName))
+      {
+        if (serviceurl == null || serviceurl.equals(svc.getHostURL()))
+        {
+          response = svc;
+          break;
+        }
+      }
+    }
+    return response;
+  }
+
+  public void setPreferredServiceFor(AlignFrame af, String serviceName,
+          String serviceAction, ServiceWithParameters selectedServer)
+  {
+    // TODO: pull out and generalise for the selectedServer's attributes
+    String afid = (af == null) ? "" : af.getViewport().getSequenceSetId();
+    if (preferredServiceMap == null)
+    {
+      preferredServiceMap = new HashMap<>();
+    }
+    Map<String, String> prefmap = preferredServiceMap.get(afid);
+    if (prefmap == null)
+    {
+      prefmap = new HashMap<>();
+      preferredServiceMap.put(afid, prefmap);
+    }
+    prefmap.put(serviceName, selectedServer.getHostURL());
+    prefmap.put(serviceAction, selectedServer.getHostURL());
+  }
+
+  public void setPreferredServiceFor(String serviceType,
+          String serviceAction, ServiceWithParameters selectedServer)
+  {
+    setPreferredServiceFor(null, serviceType, serviceAction,
+            selectedServer);
+  }
+
+  public boolean contains(ServiceWithParameters service)
+  {
+    return ourServices.contains(service);
+  }
+
+}