JAL-3878 Create WebServiceMenuBuilder which will replace PreferredServiceRegistry
[jalview.git] / src / jalview / gui / WebServicesMenuBuilder.java
1 package jalview.gui;
2
3 import java.awt.Color;
4 import java.util.ArrayList;
5 import java.util.Collection;
6 import java.util.Comparator;
7 import java.util.HashMap;
8 import java.util.HashSet;
9 import java.util.List;
10 import java.util.Map;
11 import java.util.concurrent.CopyOnWriteArrayList;
12
13 import javax.swing.JMenu;
14 import javax.swing.JMenuItem;
15
16 import jalview.gui.AlignFrame;
17 import jalview.gui.Desktop;
18 import jalview.util.MessageManager;
19 import jalview.ws2.WebServiceI;
20 import jalview.ws2.operations.Operation;
21
22 public class WebServicesMenuBuilder
23 {
24   @FunctionalInterface
25   public static interface SelectedHostChangeListener
26   {
27     public void selectedHostChanged(String opName, Operation op);
28   }
29
30   List<Operation> operations = new ArrayList<>();
31
32   /**
33    * Mapping of service name to the preferred url this service is hosted at.
34    */
35   Map<String, String> selectedHost = new HashMap<>();
36
37   public void addOperation(Operation op)
38   {
39     operations.add(op);
40   }
41
42   public void addAllOperations(Collection<Operation> operations)
43   {
44     this.operations.addAll(operations);
45   }
46
47   public void removeOperation(Operation op)
48   {
49     operations.remove(op);
50   }
51
52   public void clearOperations()
53   {
54     operations.clear();
55   }
56
57   public void buildMenu(JMenu atMenu, AlignFrame frame)
58   {
59     Map<String, List<Operation>> oneshotOperations = new HashMap<>();
60     Map<String, List<Operation>> interactiveOperations = new HashMap<>();
61     for (Operation op : operations)
62     {
63       var mapping = op.isInteractive() ? interactiveOperations : oneshotOperations;
64       if (!mapping.containsKey(op.getTypeName()))
65         mapping.put(op.getTypeName(), new ArrayList<>());
66       mapping.get(op.getTypeName()).add(op);
67     }
68     var keysSet = new HashSet<>(oneshotOperations.keySet());
69     keysSet.addAll(interactiveOperations.keySet());
70     var keys = new ArrayList<>(keysSet);
71     keys.sort(Comparator.<String>naturalOrder());
72     for (String opType : keys)
73     {
74       var submenu = new JMenu(opType);
75       var oneshot = oneshotOperations.get(opType);
76       if (oneshot != null)
77         addOneshotOperations(oneshot, submenu, frame);
78       var interactive = interactiveOperations.get(opType);
79       if (interactive != null)
80       {
81         if (oneshot != null)
82           submenu.addSeparator();
83         addInteractiveOperations(interactive, submenu, frame);
84       }
85       atMenu.add(submenu);
86     }
87   }
88
89   private void addOneshotOperations(List<Operation> operations, JMenu submenu,
90           AlignFrame frame)
91   {
92     operations = new ArrayList<>(operations);
93     operations.sort(Comparator
94             .<Operation, String>comparing(o -> o.getHostName())
95             .<String>thenComparing(o -> o.getName()));
96     String lastHost = null;
97     for (final Operation op : operations)
98     {
99       String host = op.getHostName();
100       if (lastHost != host)
101       {
102         if (lastHost != null)
103           submenu.addSeparator();
104         var menuItem = new JMenuItem(host);
105         menuItem.setForeground(Color.blue);
106         menuItem.addActionListener(e -> Desktop.showUrl(host));
107         submenu.add(menuItem);
108         lastHost = host;
109       }
110       submenu.addSeparator();
111       op.getMenuBuilder().buildMenu(submenu, frame);
112     }
113   }
114
115   private void addInteractiveOperations(List<Operation> operations,
116       JMenu submenu, AlignFrame frame)
117   {
118     Map<String, List<Operation>> groupedOperations = new HashMap<>();
119     for (Operation op : operations)
120     {
121       if (!groupedOperations.containsKey(op.getName()))
122         groupedOperations.put(op.getName(), new ArrayList<>());
123       groupedOperations.get(op.getName()).add(op);
124     }
125     var keys = new ArrayList<>(groupedOperations.keySet());
126     keys.sort(Comparator.<String>naturalOrder());
127     for (String opName : keys)
128     {
129       var ops = groupedOperations.get(opName);
130       var selectedHost = getSelectedHost(opName);
131       Operation selectedOperation = null;
132       for (var op : ops)
133       {
134         if (op.getHostName().equals(selectedHost))
135         {
136           selectedOperation = op;
137           break;
138         }
139       }
140       if (selectedOperation == null || selectedHost == null)
141         selectedOperation = ops.get(0);
142       {
143         final var hostName = selectedOperation.getHostName();
144         var hostItem = new JMenuItem(hostName);
145         hostItem.setForeground(Color.blue);
146         hostItem.addActionListener(e -> Desktop.showUrl(hostName));
147         submenu.add(hostItem);
148       }
149       selectedOperation.getMenuBuilder().buildMenu(submenu, frame);
150       if (ops.size() > 1)
151       {
152         JMenuItem alternatesMenu = new JMenuItem(MessageManager.getString("label.switch_server"));
153         submenu.add(alternatesMenu);
154         alternatesMenu.setToolTipText(JvSwingUtils.wrapTooltip(false,
155             MessageManager.getString("label.choose_jabaws_server")));
156         for (final Operation op : ops)
157         {
158           if (op == selectedOperation) continue;
159           var hostItem = new JMenuItem(op.getHostName());
160           hostItem.setForeground(Color.blue);
161           hostItem.addActionListener(e -> {
162             setSelectedHost(op.getName(), op.getHostName());
163             fireSelectedHostChanged(op.getName(), op);
164           });
165           submenu.add(hostItem);
166         }
167       }
168     }
169   }
170
171   public String getSelectedHost(String serviceName)
172   {
173     return selectedHost.getOrDefault(serviceName, null);
174   }
175
176   public void setSelectedHost(WebServiceI service)
177   {
178     setSelectedHost(service.getName(), service.getHostName());
179   }
180
181   public void setSelectedHost(String serviceName, String hostName)
182   {
183     selectedHost.put(serviceName, hostName);
184   }
185
186   List<SelectedHostChangeListener> hostChangeListeners = new CopyOnWriteArrayList<>();
187
188   public void addSelectedHostChangeListener(SelectedHostChangeListener l)
189   {
190     hostChangeListeners.add(l);
191   }
192
193   private void fireSelectedHostChanged(String opName, Operation op)
194   {
195     for (var listener : hostChangeListeners)
196     {
197       listener.selectedHostChanged(opName, op);
198     }
199   }
200
201 }