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