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