9e7438c80a418218b33fb9fb790bae1aee553790
[jalview.git] / src / jalview / ws / jws2 / PreferredServiceRegistry.java
1 package jalview.ws.jws2;
2
3 import jalview.bin.Cache;
4 import jalview.gui.AlignFrame;
5 import jalview.gui.Desktop;
6 import jalview.gui.JvSwingUtils;
7 import jalview.util.MessageManager;
8 import jalview.ws.api.ServiceWithParameters;
9
10 import java.awt.Color;
11 import java.awt.event.ActionEvent;
12 import java.awt.event.ActionListener;
13 import java.beans.PropertyChangeSupport;
14 import java.util.ArrayList;
15 import java.util.Arrays;
16 import java.util.HashMap;
17 import java.util.HashSet;
18 import java.util.Hashtable;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Vector;
22
23 import javax.swing.JMenu;
24 import javax.swing.JMenuItem;
25
26 public class PreferredServiceRegistry
27 {
28
29   private static PreferredServiceRegistry us = new PreferredServiceRegistry();
30
31   public static PreferredServiceRegistry getRegistry()
32   {
33     if (us == null)
34     {
35       us = new PreferredServiceRegistry();
36     }
37     return us;
38   }
39
40   List<ServiceWithParameters> ourServices = new ArrayList();
41
42   /**
43    * forget about any known services
44    */
45   public void clearServices()
46   {
47     ourServices.clear();
48   }
49
50   public void populateWSMenuEntry(List<ServiceWithParameters> services,
51           PropertyChangeSupport changeSupport,
52           JMenu jws2al, final AlignFrame alignFrame, String typeFilter)
53   {
54     /**
55      * eventually, JWS2 services will appear under the same align/etc submenus.
56      * for moment we keep them separate.
57      */
58     JMenu atpoint;
59     List<ServiceWithParameters> enumerableServices = new ArrayList<>();
60     // jws2al.removeAll();
61     Map<String, ServiceWithParameters> preferredHosts = new HashMap<>();
62     Map<String, List<ServiceWithParameters>> alternates = new HashMap<>();
63     for (ServiceWithParameters service : services)
64     {
65       ourServices.add(service);
66       // TODO: check this behaves with refactored serviceType to getName
67       if (!service.isInteractiveUpdate())
68       {
69         // add 'one shot' services to be displayed using the classic menu
70         // structure
71         enumerableServices.add(service);
72       }
73       else
74       {
75         if (!preferredHosts.containsKey(service.getName()))
76         {
77           ServiceWithParameters preferredInstance = getPreferredServiceFor(
78                   alignFrame, service.getName());
79           if (preferredInstance != null)
80           {
81             preferredHosts.put(service.getName(), preferredInstance);
82           }
83           else
84           {
85             preferredHosts.put(service.getName(), service);
86           }
87         }
88         List<ServiceWithParameters> ph = alternates.get(service.getName());
89         if (preferredHosts.get(service.getName()) != service)
90         {
91           if (ph == null)
92           {
93             ph = new ArrayList<>();
94           }
95           ph.add(service);
96           alternates.put(service.getName(), ph);
97         }
98       }
99
100     }
101
102     // create GUI element for classic services
103     addEnumeratedServices(jws2al, alignFrame, enumerableServices);
104     // and the instantaneous services
105     for (final ServiceWithParameters service : preferredHosts.values())
106     {
107       atpoint = JvSwingUtils.findOrCreateMenu(jws2al,
108               service.getServiceType());
109       JMenuItem hitm;
110       if (atpoint.getItemCount() > 1)
111       {
112         // previous service of this type already present
113         atpoint.addSeparator();
114       }
115       atpoint.add(hitm = new JMenuItem(service.getHostURL()));
116       hitm.setForeground(Color.blue);
117       hitm.addActionListener(new ActionListener()
118       {
119
120         @Override
121         public void actionPerformed(ActionEvent e)
122         {
123           Desktop.showUrl(service.getHostURL());
124         }
125       });
126       hitm.setToolTipText(JvSwingUtils.wrapTooltip(false,
127               MessageManager.getString("label.open_jabaws_web_page")));
128
129       service.attachWSMenuEntry(atpoint, alignFrame);
130       if (alternates.containsKey(service.getName()))
131       {
132         atpoint.add(hitm = new JMenu(
133                 MessageManager.getString("label.switch_server")));
134         hitm.setToolTipText(JvSwingUtils.wrapTooltip(false,
135                 MessageManager.getString("label.choose_jabaws_server")));
136         for (final ServiceWithParameters sv : alternates
137                 .get(service.getName()))
138         {
139           JMenuItem itm;
140           hitm.add(itm = new JMenuItem(sv.getHostURL()));
141           itm.setForeground(Color.blue);
142           itm.addActionListener(new ActionListener()
143           {
144
145             @Override
146             public void actionPerformed(ActionEvent arg0)
147             {
148               new Thread(new Runnable()
149               {
150                 @Override
151                 public void run()
152                 {
153                   setPreferredServiceFor(alignFrame, sv.getName(),
154                           sv.getServiceType(), sv);
155                   changeSupport.firePropertyChange("services",
156                           new Vector<ServiceWithParameters>(), services);
157                 };
158               }).start();
159
160             }
161           });
162         }
163       }
164     }
165   }
166
167   /**
168    * add services using the Java 2.5/2.6/2.7 system which optionally creates
169    * submenus to index by host and service program type
170    */
171   private void addEnumeratedServices(final JMenu jws2al,
172           final AlignFrame alignFrame,
173           List<ServiceWithParameters> enumerableServices)
174   {
175     boolean byhost = Cache.getDefault("WSMENU_BYHOST", false),
176             bytype = Cache.getDefault("WSMENU_BYTYPE", false);
177     /**
178      * eventually, JWS2 services will appear under the same align/etc submenus.
179      * for moment we keep them separate.
180      */
181     JMenu atpoint;
182
183     List<String> hostLabels = new ArrayList<>();
184     Hashtable<String, String> lasthostFor = new Hashtable<>();
185     Hashtable<String, ArrayList<ServiceWithParameters>> hosts = new Hashtable<>();
186     ArrayList<String> hostlist = new ArrayList<>();
187     for (ServiceWithParameters service : enumerableServices)
188     {
189       ArrayList<ServiceWithParameters> hostservices = hosts
190               .get(service.getHostURL());
191       if (hostservices == null)
192       {
193         hosts.put(service.getHostURL(), hostservices = new ArrayList<>());
194         hostlist.add(service.getHostURL());
195       }
196       hostservices.add(service);
197     }
198     // now add hosts in order of the given array
199     for (String host : hostlist)
200     {
201       ServiceWithParameters orderedsvcs[] = hosts.get(host)
202               .toArray(new ServiceWithParameters[1]);
203       String sortbytype[] = new String[orderedsvcs.length];
204       for (int i = 0; i < sortbytype.length; i++)
205       {
206         sortbytype[i] = orderedsvcs[i].getName();
207       }
208       jalview.util.QuickSort.sort(sortbytype, orderedsvcs);
209       for (final ServiceWithParameters service : orderedsvcs)
210       {
211         atpoint = JvSwingUtils.findOrCreateMenu(jws2al,
212                 service.getAction());
213         String type = service.getName();
214         if (byhost)
215         {
216           atpoint = JvSwingUtils.findOrCreateMenu(atpoint, host);
217           if (atpoint.getToolTipText() == null)
218           {
219             atpoint.setToolTipText(MessageManager
220                     .formatMessage("label.services_at", new String[]
221                     { host }));
222           }
223         }
224         if (bytype)
225         {
226           atpoint = JvSwingUtils.findOrCreateMenu(atpoint, type);
227           if (atpoint.getToolTipText() == null)
228           {
229             atpoint.setToolTipText(service.getActionText());
230           }
231         }
232         if (!byhost && !hostLabels.contains(
233                 host + service.getName() + service.getActionText()))
234         // !hostLabels.contains(host + (bytype ?
235         // service.serviceType+service.getActionText() : "")))
236         {
237           // add a marker indicating where this service is hosted
238           // relies on services from the same host being listed in a
239           // contiguous
240           // group
241           JMenuItem hitm;
242           if (hostLabels.contains(host))
243           {
244             atpoint.addSeparator();
245           }
246           else
247           {
248             hostLabels.add(host);
249           }
250           if (lasthostFor.get(service.getAction()) == null
251                   || !lasthostFor.get(service.getAction()).equals(host))
252           {
253             atpoint.add(hitm = new JMenuItem(host));
254             hitm.setForeground(Color.blue);
255             hitm.addActionListener(new ActionListener()
256             {
257
258               @Override
259               public void actionPerformed(ActionEvent e)
260               {
261                 Desktop.showUrl(service.getHostURL());
262               }
263             });
264             hitm.setToolTipText(
265                     JvSwingUtils.wrapTooltip(true, MessageManager
266                             .getString("label.open_jabaws_web_page")));
267             lasthostFor.put(service.getAction(), host);
268           }
269           hostLabels
270                   .add(host + service.getName() + service.getActionText());
271         }
272
273         service.attachWSMenuEntry(atpoint, alignFrame);
274       }
275     }
276   }
277
278   /**
279    * pick the user's preferred service based on a set of URLs (jaba server
280    * locations) and service URIs (specifying version and service interface
281    * class)
282    * 
283    * @param serviceURL
284    * @return null or best match for given uri/ls.
285    */
286   public ServiceWithParameters getPreferredServiceFor(String[] serviceURLs)
287   {
288     HashSet<String> urls = new HashSet<>();
289     urls.addAll(Arrays.asList(serviceURLs));
290     ServiceWithParameters match = null;
291
292     if (ourServices != null)
293     {
294       for (ServiceWithParameters svc : ourServices)
295       {
296         // TODO getNameURI Should return a versioned URI for the service, but
297         // doesn't as of 2.11
298         if (urls.contains(svc.getNameURI()))
299         {
300           if (match == null)
301           {
302             // for moment we always pick service from server ordered first in
303             // user's preferences
304             match = svc;
305           }
306           if (urls.contains(svc.getUri()))
307           {
308             // stop and return - we've matched type URI and URI for service
309             // endpoint
310             return svc;
311           }
312         }
313       }
314     }
315     return match;
316   }
317
318   Map<String, Map<String, String>> preferredServiceMap = new HashMap<>();
319
320   /**
321    * get current preferred endpoint of the given Jabaws service, or global
322    * default
323    * 
324    * @param af
325    *          null or a specific alignFrame
326    * @param serviceName
327    *          ServiceWithParameters.getName() for service
328    * @return null if no service of this type is available, the preferred service
329    *         for the serviceType and af if specified and if defined.
330    */
331   public ServiceWithParameters getPreferredServiceFor(AlignFrame af,
332           String serviceName)
333   {
334     String serviceurl = null;
335     synchronized (preferredServiceMap)
336     {
337       String afid = (af == null) ? "" : af.getViewport().getSequenceSetId();
338       Map<String, String> prefmap = preferredServiceMap.get(afid);
339       if (afid.length() > 0 && prefmap == null)
340       {
341         // recover global setting, if any
342         prefmap = preferredServiceMap.get("");
343       }
344       if (prefmap != null)
345       {
346         serviceurl = prefmap.get(serviceName);
347       }
348
349     }
350     ServiceWithParameters response = null;
351     for (ServiceWithParameters svc : ourServices)
352     {
353       if (svc.getName().equals(serviceName))
354       {
355         if (serviceurl == null || serviceurl.equals(svc.getHostURL()))
356         {
357           response = svc;
358           break;
359         }
360       }
361     }
362     return response;
363   }
364
365   public void setPreferredServiceFor(AlignFrame af, String serviceName,
366           String serviceAction, ServiceWithParameters selectedServer)
367   {
368     // TODO: pull out and generalise for the selectedServer's attributes
369     String afid = (af == null) ? "" : af.getViewport().getSequenceSetId();
370     if (preferredServiceMap == null)
371     {
372       preferredServiceMap = new HashMap<>();
373     }
374     Map<String, String> prefmap = preferredServiceMap.get(afid);
375     if (prefmap == null)
376     {
377       prefmap = new HashMap<>();
378       preferredServiceMap.put(afid, prefmap);
379     }
380     prefmap.put(serviceName, selectedServer.getHostURL());
381     prefmap.put(serviceAction, selectedServer.getHostURL());
382   }
383
384   public void setPreferredServiceFor(String serviceType,
385           String serviceAction, ServiceWithParameters selectedServer)
386   {
387     setPreferredServiceFor(null, serviceType, serviceAction,
388             selectedServer);
389   }
390
391   public boolean contains(ServiceWithParameters service)
392   {
393     return ourServices.contains(service);
394   }
395
396 }