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.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 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 ourServices = new ArrayList<>(); /** * forget about any known services */ public void clearServices() { ourServices.clear(); } public void populateWSMenuEntry(List services, PreferredServiceChangeListener changeListener, JMenu menu, final AlignFrame alignFrame, String typeFilter) { /** * eventually, JWS2 services will appear under the same align/etc submenus. * for moment we keep them separate. */ ourServices.addAll(services); JMenu atpoint; List oneshotServices = new ArrayList<>(); List interactiveServices = new ArrayList<>(); Map preferredHosts = new HashMap<>(); Map> alternates = new HashMap<>(); for (var service : services) { if (service.isInteractiveUpdate()) interactiveServices.add(service); else oneshotServices.add(service); } for (var service : interactiveServices) { if (!preferredHosts.containsKey(service.getName())) { var preferred = getPreferredServiceFor(alignFrame, service.getName()); preferredHosts.put(service.getName(), (preferred != null) ? preferred : service); } var ph = alternates.getOrDefault(service.getName(), new ArrayList<>()); if (!preferredHosts.containsValue(service)) { ph.add(service); alternates.putIfAbsent(service.getName(), ph); } } // create GUI element for classic services addEnumeratedServices(menu, alignFrame, oneshotServices); // and the instantaneous services for (final ServiceWithParameters service : preferredHosts.values()) { atpoint = JvSwingUtils.findOrCreateMenu(menu, service.getServiceType()); if (atpoint.getItemCount() > 1) { // previous service of this type already present atpoint.addSeparator(); } JMenuItem hitm; atpoint.add(hitm = new JMenuItem(service.getHostURL())); hitm.setForeground(Color.blue); hitm.addActionListener(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(e -> { setPreferredServiceFor(alignFrame, sv.getName(), sv.getServiceType(), sv); changeListener.preferredServiceChanged(sv); }); } } } } /** * 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 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 hostLabels = new ArrayList<>(); Hashtable lasthostFor = new Hashtable<>(); Hashtable> hosts = new Hashtable<>(); ArrayList hostlist = new ArrayList<>(); for (ServiceWithParameters service : enumerableServices) { ArrayList 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 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> 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 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 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); } }