2507ceb1025d27d82ac1518cc6a385784856928d
[jalview.git] / src / jalview / gui / RestServiceEditorPane.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.0b1)
3  * Copyright (C) 2014 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 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  * The Jalview Authors are detailed in the 'AUTHORS' file.
18  */
19 package jalview.gui;
20
21 import jalview.io.packed.DataProvider.JvDataType;
22 import jalview.jbgui.GRestServiceEditorPane;
23 import jalview.util.MessageManager;
24 import jalview.ws.rest.InputType;
25 import jalview.ws.rest.RestServiceDescription;
26
27 import java.awt.BorderLayout;
28 import java.awt.FlowLayout;
29 import java.awt.event.ActionEvent;
30 import java.awt.event.ActionListener;
31 import java.awt.event.ComponentEvent;
32 import java.awt.event.ComponentListener;
33 import java.awt.event.KeyEvent;
34 import java.awt.event.KeyListener;
35 import java.awt.event.MouseEvent;
36 import java.util.HashMap;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Vector;
40 import java.util.regex.Matcher;
41 import java.util.regex.Pattern;
42
43 import javax.swing.JFrame;
44 import javax.swing.JMenuItem;
45 import javax.swing.JPanel;
46 import javax.swing.JPopupMenu;
47 import javax.swing.event.ChangeEvent;
48 import javax.swing.event.ChangeListener;
49
50 public class RestServiceEditorPane extends GRestServiceEditorPane
51 {
52   /**
53    * the latest version of the service definition.
54    */
55   jalview.ws.rest.RestServiceDescription currentservice = null;
56
57   /**
58    * original service passed to editor if we are modifying an existing service
59    * definition
60    */
61   jalview.ws.rest.RestServiceDescription oldservice = null;
62
63   public RestServiceEditorPane()
64   {
65     super();
66     // begin with initial text description box enabled.
67     urldesc.addKeyListener(new KeyListener()
68     {
69       @Override
70       public void keyTyped(KeyEvent e)
71       {
72       }
73
74       @Override
75       public void keyReleased(KeyEvent e)
76       {
77         refreshCutnPaste(true);
78       }
79
80       @Override
81       public void keyPressed(KeyEvent e)
82       {
83
84       }
85     });
86     panels.addChangeListener(new ChangeListener()
87     {
88
89       /**
90        * last panel selected - used to decide whether the service or the GUI has
91        * the latest info
92        */
93       Object lastComp;
94
95       @Override
96       public void stateChanged(ChangeEvent e)
97       {
98         if (lastComp != paste)
99         {
100           updateServiceFromGui();
101           refreshCutnPaste(false);
102         }
103         else
104         {
105           refreshCutnPaste(true);
106         }
107         lastComp = panels.getSelectedComponent();
108
109       }
110     });
111     currentservice = new RestServiceDescription("Analysis",
112             "service description", "service name", "http://localhost/", "",
113             null, false, false, '-');
114     initGuiWith(currentservice);
115     refreshCutnPaste(false);
116     updateButtons();
117   }
118
119   public RestServiceEditorPane(RestServiceDescription toedit)
120   {
121     this();
122     oldservice = toedit;
123     if (oldservice != null)
124     {
125       currentservice = new RestServiceDescription(toedit);
126     }
127     else
128     {
129       currentservice = new RestServiceDescription("Analysis",
130               "service description", "service name", "http://localhost/",
131               "", null, false, false, '-');
132     }
133     initGuiWith(currentservice);
134     refreshCutnPaste(false);
135     updateButtons();
136   }
137
138   /**
139    * refresh the buttons based on model state
140    */
141   public void updateButtons()
142   {
143     cancelButton.setEnabled(true);
144     okButton.setEnabled(currentservice != null && currentservice.isValid());
145
146   }
147
148   Vector<String> _iparam = new Vector<String>();
149
150   Vector<String> _rparam = new Vector<String>();
151
152   /**
153    * generate an editable URL service string and parameter list using the
154    * service
155    * 
156    * @param currentservice2
157    */
158   private void initGuiWith(RestServiceDescription currentservice)
159   {
160     _iparam.clear();
161     _rparam.clear();
162     action.removeAllItems();
163     action.addItem("Alignment");
164     action.addItem("Analysis");
165     gapChar.removeAllItems();
166     gapChar.addItem(".");
167     gapChar.addItem(" ");
168     gapChar.addItem("-");
169     if (currentservice == null)
170     {
171       name.setText("");
172       descr.setText("");
173       url.setText("");
174       urlsuff.setText("");
175       action.setSelectedItem("Analysis");
176       gapChar.setSelectedItem("-");
177     }
178     else
179     {
180       name.setText(currentservice.getName());
181       descr.setText(currentservice.getDescription());
182       url.setText(currentservice.getPostUrl());
183       urlsuff.setText(currentservice.getUrlSuffix());
184       for (Map.Entry<String, InputType> inparam : currentservice
185               .getInputParams().entrySet())
186       {
187         _iparam.add(inparam.getKey() + " "
188                 + inparam.getValue().getURLtokenPrefix() + ":"
189                 + inparam.getValue().getURLEncodedParameter().toString());
190       }
191
192       for (JvDataType oparam : currentservice.getResultDataTypes())
193       {
194         _rparam.add(oparam.name());
195       }
196       iprms.setListData(_iparam);
197       rdata.setListData(_rparam);
198
199       action.setSelectedItem(currentservice.getAction());
200
201       gapChar.setSelectedItem("" + currentservice.getGapCharacter());
202     }
203     revalidate();
204   }
205
206   private String getSelectedInputToken()
207   {
208     if (iprms.getSelectedIndex() > -1)
209     {
210       String toktoedit = (String) iprms.getSelectedValue();
211       toktoedit = toktoedit.substring(0, toktoedit.indexOf(" "));
212       return toktoedit;
213     }
214     return null;
215   }
216
217   @Override
218   protected void iprmListSelection_doubleClicked()
219   {
220     String toktoedit = getSelectedInputToken();
221     if (toktoedit != null)
222     {
223       InputType toedit = currentservice.getInputParams().get(toktoedit);
224       String oldParam = toktoedit;
225       RestInputParamEditDialog dialog = new RestInputParamEditDialog(this,
226               currentservice, toedit);
227       if (dialog.wasUpdated())
228       {
229         currentservice.getInputParams().remove(oldParam);
230         currentservice.getInputParams().put(dialog.current.token,
231                 dialog.current);
232         initGuiWith(currentservice);
233       }
234
235     }
236   }
237
238   @Override
239   protected void iprmsAdd_actionPerformed(ActionEvent e)
240   {
241     RestInputParamEditDialog dialog = new RestInputParamEditDialog(this,
242             currentservice, "param"
243                     + (1 + currentservice.getInputParams().size()));
244     if (dialog.wasUpdated())
245     {
246       currentservice.getInputParams().put(dialog.current.token,
247               dialog.current);
248       initGuiWith(currentservice);
249     }
250
251   }
252
253   @Override
254   protected void iprmsRem_actionPerformed(ActionEvent e)
255   {
256     String toktoedit = getSelectedInputToken();
257     if (toktoedit != null)
258     {
259       currentservice.getInputParams().remove(toktoedit);
260       initGuiWith(currentservice);
261
262     }
263   }
264
265   @Override
266   protected void rdata_rightClicked(MouseEvent mouse)
267   {
268     final int rdatasel = rdata.getSelectedIndex();
269     if (rdatasel > -1)
270     {
271       JPopupMenu popup = new JPopupMenu(MessageManager.getString("label.select_return_type"));
272       for (final JvDataType type : JvDataType.values())
273       {
274         popup.add(new JMenuItem(type.name())).addActionListener(
275                 new ActionListener()
276                 {
277
278                   @Override
279                   public void actionPerformed(ActionEvent e)
280                   {
281                     currentservice.getResultDataTypes().set(rdatasel, type);
282                     initGuiWith(currentservice);
283                     rdata.setSelectedIndex(rdatasel);
284                   }
285                 });
286       }
287       popup.show(rdata, mouse.getX(), mouse.getY());
288     }
289   }
290
291   @Override
292   protected void rdataAdd_actionPerformed(ActionEvent e)
293   {
294     int p;
295     if ((p = rdata.getSelectedIndex()) > -1)
296     {
297       currentservice.getResultDataTypes().add(p + 1, JvDataType.ANNOTATION);
298     }
299     else
300     {
301       currentservice.addResultDatatype(JvDataType.ANNOTATION);
302     }
303     initGuiWith(currentservice);
304     rdata.setSelectedIndex(p == -1 ? currentservice.getResultDataTypes()
305             .size() - 1 : p + 1);
306   }
307
308   @Override
309   protected void rdataNdown_actionPerformed(ActionEvent e)
310   {
311     int p;
312     if ((p = rdata.getSelectedIndex()) > -1 && p < _rparam.size() - 1)
313     {
314       List<JvDataType> rtypes = currentservice.getResultDataTypes();
315       JvDataType below = rtypes.get(p + 1);
316       rtypes.set(p + 1, rtypes.get(p));
317       rtypes.set(p, below);
318       initGuiWith(currentservice);
319       rdata.setSelectedIndex(p + 1);
320     }
321   }
322
323   @Override
324   protected void rdataNup_actionPerformed(ActionEvent e)
325   {
326     int p;
327     if ((p = rdata.getSelectedIndex()) > 0)
328     {
329       List<JvDataType> rtypes = currentservice.getResultDataTypes();
330       JvDataType above = rtypes.get(p - 1);
331       rtypes.set(p - 1, rtypes.get(p));
332       rtypes.set(p, above);
333       initGuiWith(currentservice);
334       rdata.setSelectedIndex(p - 1);
335     }
336   }
337
338   @Override
339   protected void rdataRem_actionPerformed(ActionEvent e)
340   {
341     if (rdata.getSelectedIndex() > -1)
342     {
343       currentservice.getResultDataTypes().remove(rdata.getSelectedIndex());
344       initGuiWith(currentservice);
345     }
346   }
347
348   private boolean updateServiceFromGui()
349   {
350     Map<String, InputType> inputTypes = new HashMap<String, InputType>();
351     StringBuffer warnings = new StringBuffer();
352     for (String its : _iparam)
353     {
354       Matcher mtch = Pattern.compile("(\\S+)\\s(\\S+):\\[(.+)]").matcher(
355               its);
356       if (mtch.find())
357       {
358         if (!RestServiceDescription.parseTypeString(mtch.group(2) + ":"
359                 + mtch.group(3), mtch.group(1), mtch.group(2),
360                 mtch.group(3), inputTypes, warnings))
361         {
362           System.err
363                   .println("IMPLEMENTATION PROBLEM: Cannot parse RestService input parameter string '"
364                           + its + "'" + "\n" + warnings);
365         }
366       }
367     }
368     char gc = gapChar.getSelectedItem() == null ? ' ' : ((String) gapChar
369             .getSelectedItem()).charAt(0);
370     RestServiceDescription newService = new RestServiceDescription(
371             (String) action.getSelectedItem(), descr.getText().trim(), name
372                     .getText().trim(), url.getText().trim(), urlsuff
373                     .getText().trim(), inputTypes, hSeparable.isSelected(),
374             vSeparable.isSelected(), gc);
375
376     if (newService.isValid())
377     {
378       for (String its : _rparam)
379       {
380         JvDataType dtype;
381         try
382         {
383           dtype = JvDataType.valueOf(its);
384           newService.addResultDatatype(dtype);
385         } catch (Throwable x)
386         {
387
388           System.err
389                   .println("IMPLEMENTATION PROBLEM: Cannot parse RestService output parameter string '"
390                           + its + "'" + "\n" + warnings);
391         }
392       }
393       currentservice = newService;
394       return true;
395     }
396     else
397     {
398       System.err
399               .println("IMPLEMENTATION PROBLEM: Restservice generated from GUI is invalid\n"
400                       + warnings);
401
402     }
403     return false;
404   }
405
406   protected void refreshCutnPaste(boolean reparse)
407   {
408     if (!reparse && currentservice.isValid())
409     {
410       urldesc.setText(currentservice.toString());
411       parseWarnings.setVisible(false);
412     }
413     else
414     {
415       if (reparse)
416       {
417         String txt = urldesc.getText().trim();
418         if (txt.length() > 0)
419         {
420           RestServiceDescription rsd = null;
421           try
422           {
423             rsd = new RestServiceDescription(txt);
424             if (rsd.isValid())
425             {
426               parseWarnings.setVisible(false);
427               parseRes.setText("");
428               initGuiWith(currentservice = rsd);
429             }
430             else
431             {
432               parseRes.setText(MessageManager.formatMessage("label.parsing_failed_syntax_errors_shown_below_param", new String[]{rsd.getInvalidMessage()}));
433               parseWarnings.setVisible(true);
434             }
435           } catch (Throwable e)
436           {
437             e.printStackTrace();
438             parseRes.setText(MessageManager.formatMessage("label.parsing_failed_unrecoverable_exception_thrown_param", new String[]{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 }