Merge branch 'JAL-3878_ws-overhaul-3' into with_ws_overhaul-3
[jalview.git] / src / jalview / gui / SlivkaPreferences.java
1 package jalview.gui;
2
3 import jalview.bin.Cache;
4 import jalview.bin.Console;
5 import jalview.util.MessageManager;
6 import jalview.ws2.client.api.WebServiceDiscovererI;
7 import jalview.ws2.client.slivka.SlivkaWSDiscoverer;
8
9 import java.awt.BorderLayout;
10 import java.awt.Color;
11 import java.awt.Component;
12 import java.awt.Dimension;
13 import java.awt.Font;
14 import java.awt.event.ActionEvent;
15 import java.awt.event.ActionListener;
16 import java.awt.event.MouseAdapter;
17 import java.awt.event.MouseEvent;
18 import java.awt.event.MouseListener;
19 import java.net.MalformedURLException;
20 import java.net.URL;
21 import java.util.ArrayList;
22 import java.util.HashMap;
23 import java.util.Map;
24 import java.util.NoSuchElementException;
25 import java.util.concurrent.CancellationException;
26 import java.util.concurrent.CompletableFuture;
27
28 import javax.swing.BorderFactory;
29 import javax.swing.Box;
30 import javax.swing.BoxLayout;
31 import javax.swing.JButton;
32 import javax.swing.JOptionPane;
33 import javax.swing.JPanel;
34 import javax.swing.JProgressBar;
35 import javax.swing.JScrollPane;
36 import javax.swing.JTable;
37 import javax.swing.SwingUtilities;
38 import javax.swing.UIManager;
39 import javax.swing.table.AbstractTableModel;
40 import javax.swing.table.DefaultTableCellRenderer;
41
42
43 @SuppressWarnings("serial")
44 public class SlivkaPreferences extends JPanel
45 {
46   {
47     setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
48     setPreferredSize(new Dimension(500, 450));
49   }
50
51   WebServiceDiscovererI discoverer;
52
53   private final ArrayList<URL> urls = new ArrayList<>();
54
55   private final Map<URL, Integer> statuses = new HashMap<>();
56
57   private final AbstractTableModel urlTableModel = new AbstractTableModel()
58   {
59     final String[] columnNames = { "Service URL", "Status" };
60
61     @Override
62     public String getColumnName(int col)
63     {
64       return columnNames[col];
65     }
66
67     @Override
68     public Object getValueAt(int rowIndex, int columnIndex)
69     {
70       switch (columnIndex)
71       {
72       case 0:
73         return urls.get(rowIndex).toString();
74       case 1:
75         return statuses.getOrDefault(urls.get(rowIndex), WebServiceDiscovererI.STATUS_UNKNOWN);
76       default:
77         throw new NoSuchElementException();
78       }
79     }
80
81     @Override
82     public int getRowCount()
83     {
84       return urls.size();
85     }
86
87     @Override
88     public int getColumnCount()
89     {
90       return 2;
91     }
92   };
93
94   private class WSStatusCellRenderer extends DefaultTableCellRenderer
95   {
96     @Override
97     public Component getTableCellRendererComponent(JTable table,
98         Object value, boolean isSelected, boolean hasFocus, int row,
99         int column)
100     {
101       setHorizontalAlignment(CENTER);
102       super.getTableCellRendererComponent(table, "\u25CF", isSelected,
103           hasFocus, row, column);
104       switch ((Integer) value)
105       {
106       case WebServiceDiscovererI.STATUS_NO_SERVICES:
107         setForeground(Color.ORANGE);
108         break;
109       case WebServiceDiscovererI.STATUS_OK:
110         setForeground(Color.GREEN);
111         break;
112       case WebServiceDiscovererI.STATUS_INVALID:
113         setForeground(Color.RED);
114         break;
115       case WebServiceDiscovererI.STATUS_UNKNOWN:
116       default:
117         setForeground(Color.LIGHT_GRAY);
118       }
119       return this;
120     }
121   }
122
123   private JTable urlListTable = new JTable(urlTableModel);
124   {
125     urlListTable.getColumnModel().getColumn(1).setMaxWidth(60);
126     urlListTable.getColumnModel().getColumn(1)
127         .setCellRenderer(new WSStatusCellRenderer());
128   }
129
130   // URL control panel buttons
131   JButton newWsUrl = new JButton(
132       MessageManager.getString("label.new_service_url"));
133
134   JButton editWsUrl = new JButton(
135       MessageManager.getString("label.edit_service_url"));
136
137   JButton deleteWsUrl = new JButton(
138       MessageManager.getString("label.delete_service_url"));
139
140   JButton moveUrlUp = new JButton(
141       MessageManager.getString("action.move_up"));
142
143   JButton moveUrlDown = new JButton(
144       MessageManager.getString("action.move_down"));
145
146   private URL showEditUrlDialog(String oldUrl)
147   {
148     String input = (String) JvOptionPane
149         .showInternalInputDialog(
150             this,
151             MessageManager.getString("label.url:"),
152             UIManager.getString("OptionPane.inputDialogTitle", MessageManager.getLocale()),
153             JOptionPane.QUESTION_MESSAGE,
154             null,
155             null,
156             oldUrl);
157     if (input == null)
158     {
159       return null;
160     }
161     try
162     {
163       return new URL(input);
164     } catch (MalformedURLException ex)
165     {
166       JvOptionPane.showInternalMessageDialog(this,
167           MessageManager.getString("label.invalid_url"),
168           UIManager.getString("OptionPane.messageDialogTitle",
169               MessageManager.getLocale()),
170           JOptionPane.WARNING_MESSAGE);
171       return null;
172     }
173   }
174
175   // Button Action Listeners
176   private ActionListener newUrlAction = (ActionEvent e) -> {
177     final URL input = showEditUrlDialog("");
178     if (input != null)
179     {
180       urls.add(input);
181       reloadStatusForUrl(input);
182       urlTableModel.fireTableRowsInserted(urls.size(), urls.size());
183       discoverer.setUrls(urls);
184     }
185   };
186
187   private ActionListener editUrlAction = (ActionEvent e) -> {
188     final int i = urlListTable.getSelectedRow();
189     if (i >= 0)
190     {
191       final URL input = showEditUrlDialog(urls.get(i).toString());
192       if (input != null)
193       {
194         urls.set(i, input);
195         statuses.remove(input);
196         reloadStatusForUrl(input);
197         urlTableModel.fireTableRowsUpdated(i, i);
198         discoverer.setUrls(urls);
199       }
200     }
201   };
202
203   private ActionListener deleteUrlAction = (ActionEvent e) -> {
204     final int i = urlListTable.getSelectedRow();
205     if (i >= 0)
206     {
207       urls.remove(i);
208       statuses.remove(i);
209       urlTableModel.fireTableRowsDeleted(i, i);
210       discoverer.setUrls(urls);
211     }
212   };
213
214   private ActionListener moveUrlUpAction = (ActionEvent e) -> {
215     final int i = urlListTable.getSelectedRow();
216     if (i > 0)
217     {
218       moveTableRow(i, i - 1);
219       discoverer.setUrls(urls);
220     }
221   };
222
223   private ActionListener moveUrlDownAction = (ActionEvent e) -> {
224     final int i = urlListTable.getSelectedRow();
225     if (i >= 0 && i < urls.size() - 1)
226     {
227       moveTableRow(i, i + 1);
228       discoverer.setUrls(urls);
229     }
230   };
231
232   private MouseListener tableClickListener = new MouseAdapter()
233   {
234     final ActionEvent actionEvent = new ActionEvent(urlListTable,
235         ActionEvent.ACTION_PERFORMED, "edit");
236
237     @Override
238     public void mouseClicked(MouseEvent e)
239     {
240       if (e.getClickCount() > 1)
241       {
242         editUrlAction.actionPerformed(actionEvent);
243       }
244     }
245   };
246
247   // Setting up URL list Pane
248   {
249     Font font = new Font("Verdana", Font.PLAIN, 10);
250     JPanel urlPaneContainer = new JPanel(new BorderLayout(5, 5));
251     urlPaneContainer.setBorder(BorderFactory.createCompoundBorder(
252         BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(),
253             "Slivka Web Services"),
254         BorderFactory.createEmptyBorder(10, 5, 5, 5)));
255
256     newWsUrl.setFont(font);
257     editWsUrl.setFont(font);
258     deleteWsUrl.setFont(font);
259     moveUrlUp.setFont(font);
260     moveUrlDown.setFont(font);
261     JPanel editContainer = new JPanel();
262     editContainer.add(newWsUrl);
263     editContainer.add(editWsUrl);
264     editContainer.add(deleteWsUrl);
265     urlPaneContainer.add(editContainer, BorderLayout.PAGE_END);
266
267     JPanel moveContainer = new JPanel();
268     moveContainer
269         .setLayout(new BoxLayout(moveContainer, BoxLayout.PAGE_AXIS));
270     moveContainer.add(moveUrlUp);
271     moveContainer.add(Box.createRigidArea(new Dimension(0, 5)));
272     moveContainer.add(moveUrlDown);
273     urlPaneContainer.add(moveContainer, BorderLayout.LINE_START);
274
275     urlPaneContainer.add(new JScrollPane(urlListTable),
276         BorderLayout.CENTER);
277     this.add(urlPaneContainer);
278
279     // Connecting action listeners
280     urlListTable.addMouseListener(tableClickListener);
281     newWsUrl.addActionListener(newUrlAction);
282     editWsUrl.addActionListener(editUrlAction);
283     deleteWsUrl.addActionListener(deleteUrlAction);
284     moveUrlUp.addActionListener(moveUrlUpAction);
285     moveUrlDown.addActionListener(moveUrlDownAction);
286   }
287
288   private void moveTableRow(int fromIndex, int toIndex)
289   {
290     URL url = urls.get(fromIndex);
291     int status = statuses.get(fromIndex);
292     urls.set(fromIndex, urls.get(toIndex));
293     urls.set(toIndex, url);
294     if (urlListTable.getSelectedRow() == fromIndex)
295     {
296       urlListTable.setRowSelectionInterval(toIndex, toIndex);
297     }
298     int firstRow = Math.min(toIndex, fromIndex);
299     int lastRow = Math.max(fromIndex, toIndex);
300     urlTableModel.fireTableRowsUpdated(firstRow, lastRow);
301   }
302
303   // Discoverer reloading buttons
304   JButton refreshServices = new JButton(
305       MessageManager.getString("action.refresh_services"));
306
307   JButton resetServices = new JButton(
308       MessageManager.getString("action.reset_services"));
309
310   JProgressBar progressBar = new JProgressBar();
311
312   // Discoverer buttons action listeners
313   private ActionListener refreshServicesAction = (ActionEvent e) -> {
314     progressBar.setVisible(true);
315     Console.info("Requesting service reload");
316     discoverer.startDiscoverer().handle((services, exception) -> {
317       if (exception == null)
318       {
319         Console.info("Reloading done");
320       }
321       else if (exception instanceof CancellationException)
322       {
323         Console.info("Reloading cancelled");
324       }
325       else {
326         Console.error("Reloading failed", exception);
327       }
328       SwingUtilities.invokeLater(() -> progressBar.setVisible(false));
329       return null;
330     });
331   };
332
333   private ActionListener resetServicesAction = (ActionEvent e) -> {
334     discoverer.setUrls(null);
335     urls.clear();
336     statuses.clear();
337     urls.addAll(discoverer.getUrls());
338     for (URL url : urls)
339     {
340       reloadStatusForUrl(url);
341     }
342     urlTableModel.fireTableDataChanged();
343   };
344
345   {
346     Font font = new Font("Verdana", Font.PLAIN, 11);
347     refreshServices.setFont(font);
348     resetServices.setFont(font);
349     JPanel container = new JPanel();
350     container.add(refreshServices);
351     container.add(resetServices);
352     this.add(container);
353
354     // Connecting action listeners
355     refreshServices.addActionListener(refreshServicesAction);
356     resetServices.addActionListener(resetServicesAction);
357   }
358
359   {
360     progressBar.setVisible(false);
361     progressBar.setIndeterminate(true);
362     add(progressBar);
363   }
364
365   SlivkaPreferences()
366   {
367     // Initial URLs loading
368     discoverer = SlivkaWSDiscoverer.getInstance();
369     urls.addAll(discoverer.getUrls());
370     for (URL url : urls)
371     {
372       reloadStatusForUrl(url);
373     }
374   }
375
376   private void reloadStatusForUrl(URL url)
377   {
378     CompletableFuture.supplyAsync(() -> discoverer.getStatusForUrl(url))
379         .thenAccept((status) -> {
380           statuses.put(url, status);
381           int row = urls.indexOf(url);
382           if (row >= 0)
383             urlTableModel.fireTableCellUpdated(row, 1);
384         });
385   }
386 }