update author list in license for (JAL-826)
[jalview.git] / src / jalview / gui / RestServiceEditorPane.java
1 /*******************************************************************************
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7)
3  * Copyright (C) 2011 J Procter, AM Waterhouse, J Engelhardt, LM Lui, 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 the latest info
89        */
90       Object lastComp;
91       @Override
92       public void stateChanged(ChangeEvent e)
93       {
94         if (lastComp!=paste) {
95           updateServiceFromGui();
96           refreshCutnPaste(false);
97         } else {
98           refreshCutnPaste(true);
99         }
100         lastComp = panels.getSelectedComponent();
101         
102       }
103       });
104     currentservice = new RestServiceDescription("Analysis", "service description", "service name", "http://localhost/", "", null, false, false, '-');
105     initGuiWith(currentservice);
106     refreshCutnPaste(false);
107     updateButtons();
108   }
109
110   public RestServiceEditorPane(RestServiceDescription toedit)
111   {
112     this();
113     oldservice = toedit;
114     if (oldservice!=null)
115     {    currentservice = new RestServiceDescription(toedit);
116     } else {
117       currentservice = new RestServiceDescription("Analysis", "service description", "service name", "http://localhost/", "", null, false, false, '-');
118     }
119     initGuiWith(currentservice);
120     refreshCutnPaste(false);
121     updateButtons();
122   }
123
124   /**
125    * refresh the buttons based on model state
126    */
127   public void updateButtons()
128   {
129     cancelButton.setEnabled(true);
130     okButton.setEnabled(currentservice != null && currentservice.isValid());
131
132   }
133
134   Vector<String> _iparam = new Vector<String>();
135
136   Vector<String> _rparam = new Vector<String>();
137
138   /**
139    * generate an editable URL service string and parameter list using the
140    * service
141    * 
142    * @param currentservice2
143    */
144   private void initGuiWith(RestServiceDescription currentservice)
145   {
146     _iparam.clear();
147     _rparam.clear();
148     action.removeAllItems();
149     action.addItem("Alignment");
150     action.addItem("Analysis");
151     gapChar.removeAllItems();
152     gapChar.addItem(".");
153     gapChar.addItem(" ");
154     gapChar.addItem("-");
155     if (currentservice==null)
156     {
157       name.setText("");
158       descr.setText("");
159       url.setText("");
160       urlsuff.setText("");
161       action.setSelectedItem("Analysis");
162       gapChar.setSelectedItem("-");
163     } else {
164     name.setText(currentservice.getName());
165     descr.setText(currentservice.getDescription());
166     url.setText(currentservice.getPostUrl());
167     urlsuff.setText(currentservice.getUrlSuffix());
168     for (Map.Entry<String, InputType> inparam : currentservice
169             .getInputParams().entrySet())
170     {
171       _iparam.add(inparam.getKey() + " "
172               + inparam.getValue().getURLtokenPrefix() + ":"
173               + inparam.getValue().getURLEncodedParameter().toString());
174     }
175       
176     for (JvDataType oparam : currentservice.getResultDataTypes())
177     {
178       _rparam.add(oparam.name());
179     }
180     iprms.setListData(_iparam);
181     rdata.setListData(_rparam);
182
183     action.setSelectedItem(currentservice.getAction());
184
185     gapChar.setSelectedItem(""+currentservice.getGapCharacter());
186     }
187     revalidate();
188   }
189   private String getSelectedInputToken()
190   {
191     if (iprms.getSelectedIndex()>-1)
192     {
193     String toktoedit = (String) iprms.getSelectedValue();
194     toktoedit=toktoedit.substring(0, toktoedit.indexOf(" "));
195     return toktoedit;
196     }
197     return null;
198   }
199   @Override
200   protected void iprmListSelection_doubleClicked()
201   {
202     String toktoedit = getSelectedInputToken();
203     if (toktoedit!=null)
204     {
205       InputType toedit = currentservice.getInputParams().get(toktoedit);
206       String oldParam=toktoedit;
207       RestInputParamEditDialog dialog=new RestInputParamEditDialog(this, currentservice, toedit);
208       if (dialog.wasUpdated()) {
209         currentservice.getInputParams().remove(oldParam);
210         currentservice.getInputParams().put(dialog.current.token, dialog.current);
211         initGuiWith(currentservice);
212       }
213       
214     }
215   }
216   @Override
217   protected void iprmsAdd_actionPerformed(ActionEvent e)
218   {
219     RestInputParamEditDialog dialog=new RestInputParamEditDialog(this, currentservice, "param"+(1+currentservice.getInputParams().size()));
220     if (dialog.wasUpdated()) {
221       currentservice.getInputParams().put(dialog.current.token, dialog.current);
222       initGuiWith(currentservice);
223     }
224
225   }
226   @Override
227   protected void iprmsRem_actionPerformed(ActionEvent e)
228   {
229     String toktoedit = getSelectedInputToken();
230     if (toktoedit!=null)
231     {
232       currentservice.getInputParams().remove(toktoedit);
233       initGuiWith(currentservice);
234
235     }
236   }
237   @Override
238   protected void rdata_rightClicked(MouseEvent mouse)
239   {
240     final int rdatasel = rdata.getSelectedIndex();
241     if (rdatasel>-1)
242     {
243       JPopupMenu popup = new JPopupMenu("Select return type");
244       for (final JvDataType type:JvDataType.values()) {
245         popup.add(new JMenuItem(type.name())).addActionListener(new ActionListener()
246         {
247           
248           @Override
249           public void actionPerformed(ActionEvent e)
250           {
251             currentservice.getResultDataTypes().set(rdatasel, type);
252             initGuiWith(currentservice);   
253             rdata.setSelectedIndex(rdatasel);
254           }
255         });
256       }
257       popup.show(rdata, mouse.getX(), mouse.getY());
258     }
259   }
260   @Override
261   protected void rdataAdd_actionPerformed(ActionEvent e)
262   {
263     int p;
264     if ((p=rdata.getSelectedIndex())>-1)
265     {
266       currentservice.getResultDataTypes().add(p+1, JvDataType.ANNOTATION);
267     } else {
268       currentservice.addResultDatatype(JvDataType.ANNOTATION);
269     }
270     initGuiWith(currentservice);
271     rdata.setSelectedIndex(p==-1 ? currentservice.getResultDataTypes().size()-1 : p+1);
272   }
273   @Override
274   protected void rdataNdown_actionPerformed(ActionEvent e)
275   {
276     int p;
277     if ((p=rdata.getSelectedIndex())>-1 && p<_rparam.size()-1)
278     {
279       List<JvDataType> rtypes = currentservice.getResultDataTypes();
280       JvDataType below = rtypes.get(p+1);
281       rtypes.set(p+1, rtypes.get(p));
282       rtypes.set(p, below);
283       initGuiWith(currentservice);
284       rdata.setSelectedIndex(p+1);
285     }
286   }
287   @Override
288   protected void rdataNup_actionPerformed(ActionEvent e)
289   {
290     int p;
291     if ((p=rdata.getSelectedIndex())>0)
292     {
293       List<JvDataType> rtypes = currentservice.getResultDataTypes();
294       JvDataType above = rtypes.get(p-1);
295       rtypes.set(p-1, rtypes.get(p));
296       rtypes.set(p, above);
297       initGuiWith(currentservice);
298       rdata.setSelectedIndex(p-1);
299     }
300   }
301   @Override
302   protected void rdataRem_actionPerformed(ActionEvent e)
303   {
304     if (rdata.getSelectedIndex()>-1)
305     {
306       currentservice.getResultDataTypes().remove(rdata.getSelectedIndex());
307       initGuiWith(currentservice);
308     }
309   }
310
311   private boolean updateServiceFromGui() {
312     Map<String,InputType>inputTypes = new HashMap<String, InputType>();
313     StringBuffer warnings=new StringBuffer();
314     for (String its:_iparam)
315     {
316       Matcher mtch = Pattern.compile("(\\S+)\\s(\\S+):\\[(.+)]").matcher(its);
317       if (mtch.find()) {
318         if (!RestServiceDescription.parseTypeString(mtch.group(2)+":"+mtch.group(3), mtch.group(1), mtch.group(2),mtch.group(3), inputTypes, warnings))
319         {
320           System.err.println("IMPLEMENTATION PROBLEM: Cannot parse RestService input parameter string '"+its+"'"+"\n"+warnings);
321         }        
322       }
323     }
324     char gc = gapChar.getSelectedItem()==null ? ' ' : ((String)gapChar.getSelectedItem()).charAt(0);
325     RestServiceDescription newService = new RestServiceDescription((String) action.getSelectedItem(),
326           descr.getText().trim(), name.getText().trim(), url.getText().trim(), urlsuff.getText().trim(), inputTypes, hSeparable.isSelected(), vSeparable.isSelected(), gc);
327             
328     if (newService.isValid())
329     {
330       for (String its:_rparam)
331       {
332         JvDataType dtype;
333         try {
334           dtype = JvDataType.valueOf(its);
335           newService.addResultDatatype(dtype);
336         }
337         catch (Throwable x)
338         {
339
340           System.err.println("IMPLEMENTATION PROBLEM: Cannot parse RestService output parameter string '"+its+"'"+"\n"+warnings);
341         }
342       }
343       currentservice = newService;
344       return true;
345     } else {
346       System.err.println("IMPLEMENTATION PROBLEM: Restservice generated from GUI is invalid\n"+warnings);
347
348     }
349     return false;
350   }
351   protected void refreshCutnPaste(boolean reparse)
352   {
353     if (!reparse && currentservice.isValid())
354     {
355       urldesc.setText(currentservice.toString());
356       parseWarnings.setVisible(false);
357     }
358     else
359     {
360       if (reparse)
361       {
362         String txt = urldesc.getText().trim();
363         if (txt.length() > 0)
364         {
365           RestServiceDescription rsd = null;
366           try
367           {
368             rsd = new RestServiceDescription(txt);
369             if (rsd.isValid())
370             {
371               parseWarnings.setVisible(false);
372               parseRes.setText("");
373               initGuiWith(currentservice=rsd);
374             }
375             else
376             {
377               parseRes.setText("Parsing failed. Syntax errors shown below\n"
378                       + rsd.getInvalidMessage());
379               parseWarnings.setVisible(true);
380             }
381           } catch (Throwable e)
382           {
383             e.printStackTrace();
384             parseRes.setText("\nParsing failed. An unrecoverable exception was thrown:\n"
385                     + e.toString());
386             parseWarnings.setVisible(true);
387           }
388         }
389         paste.revalidate();
390       }
391     }
392     
393   }
394
395   public static void main(String[] args)
396   {
397     if (args.length == 0)
398     {
399       new Thread(new Runnable()
400       {
401         boolean visible = true;
402
403         public void run()
404         {
405           boolean nulserv=true;
406           while (visible)
407           {
408             final Thread runner = Thread.currentThread();
409             JFrame df = new JFrame();
410             df.getContentPane().setLayout(new BorderLayout());
411             df.getContentPane().add(
412                     (nulserv=!nulserv) ? new RestServiceEditorPane(jalview.ws.rest.RestClient
413                             .makeShmmrRestClient().getRestDescription()) : new RestServiceEditorPane(), 
414                     BorderLayout.CENTER);
415             df.setBounds(100, 100, 600, 400);
416             df.addComponentListener(new ComponentListener()
417             {
418
419               @Override
420               public void componentShown(ComponentEvent e)
421               {
422
423               }
424
425               @Override
426               public void componentResized(ComponentEvent e)
427               {
428
429               }
430
431               @Override
432               public void componentMoved(ComponentEvent e)
433               {
434
435               }
436
437               @Override
438               public void componentHidden(ComponentEvent e)
439               {
440                 visible = false;
441                 runner.interrupt();
442
443               }
444             });
445             df.setVisible(true);
446             while (visible)
447             {
448               try
449               {
450                 Thread.sleep(10000);
451               } catch (Exception x)
452               {
453               }
454               ;
455             }
456             visible = true;
457           }
458         }
459       }).start();
460
461     }
462   }
463   String finalService=null;
464   public void showDialog(String title)
465   {
466     if (oldservice!=null)
467     {
468       finalService = oldservice.toString();
469     }
470     JalviewDialog jvd = new JalviewDialog()
471     {
472       
473       @Override
474       protected void raiseClosed()
475       {
476         // TODO Auto-generated method stub
477         
478       }
479       
480       @Override
481       protected void okPressed()
482       {
483         updateServiceFromGui();
484         finalService = currentservice.toString();        
485       }
486       
487       @Override
488       protected void cancelPressed()
489       {
490         
491       }
492     };
493     JPanel pane = new JPanel(new BorderLayout()),okcancel=new JPanel(new FlowLayout());
494     pane.add(this,BorderLayout.CENTER);
495     okcancel.add(jvd.ok);
496     okcancel.add(jvd.cancel);
497     pane.add(okcancel, BorderLayout.SOUTH);
498     jvd.initDialogFrame(pane, true, true, title, 600,350);
499     jvd.waitForInput();
500   }
501
502   public String getEditedRestService()
503   {
504     return finalService;
505   }
506 }