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