Merge branch 'JAL-3878_ws-overhaul-3' into with_ws_overhaul-3
[jalview.git] / src / jalview / gui / RestServiceEditorPane.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.gui;
22
23 import jalview.io.packed.DataProvider.JvDataType;
24 import jalview.jbgui.GRestServiceEditorPane;
25 import jalview.util.MessageManager;
26 import jalview.ws.rest.InputType;
27 import jalview.ws.rest.RestServiceDescription;
28
29 import java.awt.BorderLayout;
30 import java.awt.FlowLayout;
31 import java.awt.event.ActionEvent;
32 import java.awt.event.ActionListener;
33 import java.awt.event.ComponentEvent;
34 import java.awt.event.ComponentListener;
35 import java.awt.event.KeyEvent;
36 import java.awt.event.KeyListener;
37 import java.awt.event.MouseEvent;
38 import java.util.HashMap;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.Vector;
42 import java.util.regex.Matcher;
43 import java.util.regex.Pattern;
44
45 import javax.swing.JFrame;
46 import javax.swing.JMenuItem;
47 import javax.swing.JPanel;
48 import javax.swing.JPopupMenu;
49 import javax.swing.event.ChangeEvent;
50 import javax.swing.event.ChangeListener;
51
52 public class RestServiceEditorPane extends GRestServiceEditorPane
53 {
54   /**
55    * the latest version of the service definition.
56    */
57   jalview.ws.rest.RestServiceDescription currentservice = null;
58
59   /**
60    * original service passed to editor if we are modifying an existing service
61    * definition
62    */
63   jalview.ws.rest.RestServiceDescription oldservice = null;
64
65   public RestServiceEditorPane()
66   {
67     super();
68     // begin with initial text description box enabled.
69     urldesc.addKeyListener(new KeyListener()
70     {
71       @Override
72       public void keyTyped(KeyEvent e)
73       {
74       }
75
76       @Override
77       public void keyReleased(KeyEvent e)
78       {
79         refreshCutnPaste(true);
80       }
81
82       @Override
83       public void keyPressed(KeyEvent e)
84       {
85
86       }
87     });
88     panels.addChangeListener(new ChangeListener()
89     {
90
91       /**
92        * last panel selected - used to decide whether the service or the GUI has
93        * the latest info
94        */
95       Object lastComp;
96
97       @Override
98       public void stateChanged(ChangeEvent e)
99       {
100         if (lastComp != paste)
101         {
102           updateServiceFromGui();
103           refreshCutnPaste(false);
104         }
105         else
106         {
107           refreshCutnPaste(true);
108         }
109         lastComp = panels.getSelectedComponent();
110
111       }
112     });
113     currentservice = new RestServiceDescription("Analysis",
114             "service description", "service name", "http://localhost/", "",
115             null, false, false, '-');
116     initGuiWith(currentservice);
117     refreshCutnPaste(false);
118     updateButtons();
119   }
120
121   public RestServiceEditorPane(RestServiceDescription toedit)
122   {
123     this();
124     oldservice = toedit;
125     if (oldservice != null)
126     {
127       currentservice = new RestServiceDescription(toedit);
128     }
129     else
130     {
131       currentservice = new RestServiceDescription("Analysis",
132               "service description", "service name", "http://localhost/",
133               "", null, false, false, '-');
134     }
135     initGuiWith(currentservice);
136     refreshCutnPaste(false);
137     updateButtons();
138   }
139
140   /**
141    * refresh the buttons based on model state
142    */
143   public void updateButtons()
144   {
145     cancelButton.setEnabled(true);
146     okButton.setEnabled(currentservice != null && currentservice.isValid());
147
148   }
149
150   Vector<String> _iparam = new Vector<>();
151
152   Vector<String> _rparam = new Vector<>();
153
154   /**
155    * generate an editable URL service string and parameter list using the
156    * service
157    * 
158    * @param currentservice2
159    */
160   private void initGuiWith(RestServiceDescription currentservice)
161   {
162     _iparam.clear();
163     _rparam.clear();
164     action.removeAllItems();
165     action.addItem("Alignment");
166     action.addItem("Analysis");
167     gapChar.removeAllItems();
168     gapChar.addItem(".");
169     gapChar.addItem(" ");
170     gapChar.addItem("-");
171     if (currentservice == null)
172     {
173       name.setText("");
174       descr.setText("");
175       url.setText("");
176       urlsuff.setText("");
177       action.setSelectedItem("Analysis");
178       gapChar.setSelectedItem("-");
179     }
180     else
181     {
182       name.setText(currentservice.getName());
183       descr.setText(currentservice.getDescription());
184       url.setText(currentservice.getPostUrl());
185       urlsuff.setText(currentservice.getUrlSuffix());
186       for (Map.Entry<String, InputType> inparam : currentservice
187               .getInputParams().entrySet())
188       {
189         _iparam.add(inparam.getKey() + " "
190                 + inparam.getValue().getURLtokenPrefix() + ":"
191                 + inparam.getValue().getURLEncodedParameter().toString());
192       }
193
194       for (JvDataType oparam : currentservice.getResultDataTypes())
195       {
196         _rparam.add(oparam.name());
197       }
198       iprms.setListData(_iparam);
199       rdata.setListData(_rparam);
200
201       action.setSelectedItem(currentservice.getAction());
202
203       gapChar.setSelectedItem("" + currentservice.getGapCharacter());
204     }
205     revalidate();
206   }
207
208   private String getSelectedInputToken()
209   {
210     if (iprms.getSelectedIndex() > -1)
211     {
212       String toktoedit = (String) iprms.getSelectedValue();
213       toktoedit = toktoedit.substring(0, toktoedit.indexOf(" "));
214       return toktoedit;
215     }
216     return null;
217   }
218
219   @Override
220   protected void iprmListSelection_doubleClicked()
221   {
222     String toktoedit = getSelectedInputToken();
223     if (toktoedit != null)
224     {
225       InputType toedit = currentservice.getInputParams().get(toktoedit);
226       String oldParam = toktoedit;
227       RestInputParamEditDialog dialog = new RestInputParamEditDialog(this,
228               currentservice, toedit);
229       if (dialog.wasUpdated())
230       {
231         currentservice.getInputParams().remove(oldParam);
232         currentservice.getInputParams().put(dialog.current.token,
233                 dialog.current);
234         initGuiWith(currentservice);
235       }
236
237     }
238   }
239
240   @Override
241   protected void iprmsAdd_actionPerformed(ActionEvent e)
242   {
243     RestInputParamEditDialog dialog = new RestInputParamEditDialog(this,
244             currentservice,
245             "param" + (1 + currentservice.getInputParams().size()));
246     if (dialog.wasUpdated())
247     {
248       currentservice.getInputParams().put(dialog.current.token,
249               dialog.current);
250       initGuiWith(currentservice);
251     }
252
253   }
254
255   @Override
256   protected void iprmsRem_actionPerformed(ActionEvent e)
257   {
258     String toktoedit = getSelectedInputToken();
259     if (toktoedit != null)
260     {
261       currentservice.getInputParams().remove(toktoedit);
262       initGuiWith(currentservice);
263
264     }
265   }
266
267   @Override
268   protected void rdata_rightClicked(MouseEvent mouse)
269   {
270     final int rdatasel = rdata.getSelectedIndex();
271     if (rdatasel > -1)
272     {
273       JPopupMenu popup = new JPopupMenu(
274               MessageManager.getString("label.select_return_type"));
275       for (final JvDataType type : JvDataType.values())
276       {
277         popup.add(new JMenuItem(type.name()))
278                 .addActionListener(new ActionListener()
279                 {
280
281                   @Override
282                   public void actionPerformed(ActionEvent e)
283                   {
284                     currentservice.getResultDataTypes().set(rdatasel, type);
285                     initGuiWith(currentservice);
286                     rdata.setSelectedIndex(rdatasel);
287                   }
288                 });
289       }
290       popup.show(rdata, mouse.getX(), mouse.getY());
291     }
292   }
293
294   @Override
295   protected void rdataAdd_actionPerformed(ActionEvent e)
296   {
297     int p;
298     if ((p = rdata.getSelectedIndex()) > -1)
299     {
300       currentservice.getResultDataTypes().add(p + 1, JvDataType.ANNOTATION);
301     }
302     else
303     {
304       currentservice.addResultDatatype(JvDataType.ANNOTATION);
305     }
306     initGuiWith(currentservice);
307     rdata.setSelectedIndex(
308             p == -1 ? currentservice.getResultDataTypes().size() - 1
309                     : p + 1);
310   }
311
312   @Override
313   protected void rdataNdown_actionPerformed(ActionEvent e)
314   {
315     int p;
316     if ((p = rdata.getSelectedIndex()) > -1 && p < _rparam.size() - 1)
317     {
318       List<JvDataType> rtypes = currentservice.getResultDataTypes();
319       JvDataType below = rtypes.get(p + 1);
320       rtypes.set(p + 1, rtypes.get(p));
321       rtypes.set(p, below);
322       initGuiWith(currentservice);
323       rdata.setSelectedIndex(p + 1);
324     }
325   }
326
327   @Override
328   protected void rdataNup_actionPerformed(ActionEvent e)
329   {
330     int p;
331     if ((p = rdata.getSelectedIndex()) > 0)
332     {
333       List<JvDataType> rtypes = currentservice.getResultDataTypes();
334       JvDataType above = rtypes.get(p - 1);
335       rtypes.set(p - 1, rtypes.get(p));
336       rtypes.set(p, above);
337       initGuiWith(currentservice);
338       rdata.setSelectedIndex(p - 1);
339     }
340   }
341
342   @Override
343   protected void rdataRem_actionPerformed(ActionEvent e)
344   {
345     if (rdata.getSelectedIndex() > -1)
346     {
347       currentservice.getResultDataTypes().remove(rdata.getSelectedIndex());
348       initGuiWith(currentservice);
349     }
350   }
351
352   private boolean updateServiceFromGui()
353   {
354     Map<String, InputType> inputTypes = new HashMap<>();
355     StringBuffer warnings = new StringBuffer();
356     for (String its : _iparam)
357     {
358       Matcher mtch = Pattern.compile("(\\S+)\\s(\\S+):\\[(.+)]")
359               .matcher(its);
360       if (mtch.find())
361       {
362         if (!RestServiceDescription.parseTypeString(
363                 mtch.group(2) + ":" + mtch.group(3), mtch.group(1),
364                 mtch.group(2), mtch.group(3), inputTypes, warnings))
365         {
366           System.err.println(
367                   "IMPLEMENTATION PROBLEM: Cannot parse RestService input parameter string '"
368                           + its + "'" + "\n" + warnings);
369         }
370       }
371     }
372     char gc = gapChar.getSelectedItem() == null ? ' '
373             : ((String) gapChar.getSelectedItem()).charAt(0);
374     RestServiceDescription newService = new RestServiceDescription(
375             (String) action.getSelectedItem(), descr.getText().trim(),
376             name.getText().trim(), url.getText().trim(),
377             urlsuff.getText().trim(), inputTypes, hSeparable.isSelected(),
378             vSeparable.isSelected(), gc);
379
380     if (newService.isValid())
381     {
382       for (String its : _rparam)
383       {
384         JvDataType dtype;
385         try
386         {
387           dtype = JvDataType.valueOf(its);
388           newService.addResultDatatype(dtype);
389         } catch (Throwable x)
390         {
391
392           System.err.println(
393                   "IMPLEMENTATION PROBLEM: Cannot parse RestService output parameter string '"
394                           + its + "'" + "\n" + warnings);
395         }
396       }
397       currentservice = newService;
398       return true;
399     }
400     else
401     {
402       System.err.println(
403               "IMPLEMENTATION PROBLEM: Restservice generated from GUI is invalid\n"
404                       + warnings);
405
406     }
407     return false;
408   }
409
410   protected void refreshCutnPaste(boolean reparse)
411   {
412     if (!reparse && currentservice.isValid())
413     {
414       urldesc.setText(currentservice.toString());
415       parseWarnings.setVisible(false);
416     }
417     else
418     {
419       if (reparse)
420       {
421         String txt = urldesc.getText().trim();
422         if (txt.length() > 0)
423         {
424           RestServiceDescription rsd = null;
425           try
426           {
427             rsd = new RestServiceDescription(txt);
428             if (rsd.isValid())
429             {
430               parseWarnings.setVisible(false);
431               parseRes.setText("");
432               initGuiWith(currentservice = rsd);
433             }
434             else
435             {
436               parseRes.setText(MessageManager.formatMessage(
437                       "label.parsing_failed_syntax_errors_shown_below_param",
438                       new String[]
439                       { rsd.getInvalidMessage() }));
440               parseWarnings.setVisible(true);
441             }
442           } catch (Throwable e)
443           {
444             e.printStackTrace();
445             parseRes.setText(MessageManager.formatMessage(
446                     "label.parsing_failed_unrecoverable_exception_thrown_param",
447                     new String[]
448                     { e.toString() }));
449             parseWarnings.setVisible(true);
450           }
451         }
452         paste.revalidate();
453       }
454     }
455
456   }
457
458   /**
459    * @j2sIgnore
460    * @param args
461    */
462   public static void main(String[] args)
463   {
464     if (args.length == 0)
465     {
466       new Thread(new Runnable()
467       {
468         boolean visible = true;
469
470         @Override
471         public void run()
472         {
473           boolean nulserv = true;
474           while (visible)
475           {
476             final Thread runner = Thread.currentThread();
477             JFrame df = new JFrame();
478             df.getContentPane().setLayout(new BorderLayout());
479             df.getContentPane().add(
480                     (nulserv = !nulserv) ? new RestServiceEditorPane(
481                             jalview.ws.rest.clientdefs.ShmrRestClient
482                                     .makeShmmrRestClient()
483                                     .getRestDescription())
484                             : new RestServiceEditorPane(),
485                     BorderLayout.CENTER);
486             df.setBounds(100, 100, 600, 400);
487             df.addComponentListener(new ComponentListener()
488             {
489
490               @Override
491               public void componentShown(ComponentEvent e)
492               {
493
494               }
495
496               @Override
497               public void componentResized(ComponentEvent e)
498               {
499
500               }
501
502               @Override
503               public void componentMoved(ComponentEvent e)
504               {
505
506               }
507
508               @Override
509               public void componentHidden(ComponentEvent e)
510               {
511                 visible = false;
512                 runner.interrupt();
513
514               }
515             });
516             df.setVisible(true);
517             while (visible)
518             {
519               try
520               {
521                 Thread.sleep(10000);
522               } catch (Exception x)
523               {
524               }
525             }
526             visible = true;
527           }
528         }
529       }).start();
530
531     }
532   }
533
534   String finalService = null;
535
536   public void showDialog(String title)
537   {
538     if (oldservice != null)
539     {
540       finalService = oldservice.toString();
541     }
542     JalviewDialog jvd = new JalviewDialog()
543     {
544
545       @Override
546       protected void raiseClosed()
547       {
548         // TODO Auto-generated method stub
549
550       }
551
552       @Override
553       protected void okPressed()
554       {
555         updateServiceFromGui();
556         finalService = currentservice.toString();
557       }
558
559       @Override
560       protected void cancelPressed()
561       {
562
563       }
564     };
565     JPanel pane = new JPanel(new BorderLayout()),
566             okcancel = new JPanel(new FlowLayout());
567     pane.add(this, BorderLayout.CENTER);
568     okcancel.add(jvd.ok);
569     okcancel.add(jvd.cancel);
570     pane.add(okcancel, BorderLayout.SOUTH);
571     jvd.initDialogFrame(pane, true, true, title, 600, 350);
572     jvd.waitForInput();
573   }
574
575   public String getEditedRestService()
576   {
577     return finalService;
578   }
579 }