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