Merge branch 'releases/Release_2_11_4_Branch'
[jalview.git] / 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<String>();
151
152   Vector<String> _rparam = new Vector<String>();
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, "param"
245                     + (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())).addActionListener(
278                 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(p == -1 ? currentservice.getResultDataTypes()
308             .size() - 1 : p + 1);
309   }
310
311   @Override
312   protected void rdataNdown_actionPerformed(ActionEvent e)
313   {
314     int p;
315     if ((p = rdata.getSelectedIndex()) > -1 && p < _rparam.size() - 1)
316     {
317       List<JvDataType> rtypes = currentservice.getResultDataTypes();
318       JvDataType below = rtypes.get(p + 1);
319       rtypes.set(p + 1, rtypes.get(p));
320       rtypes.set(p, below);
321       initGuiWith(currentservice);
322       rdata.setSelectedIndex(p + 1);
323     }
324   }
325
326   @Override
327   protected void rdataNup_actionPerformed(ActionEvent e)
328   {
329     int p;
330     if ((p = rdata.getSelectedIndex()) > 0)
331     {
332       List<JvDataType> rtypes = currentservice.getResultDataTypes();
333       JvDataType above = rtypes.get(p - 1);
334       rtypes.set(p - 1, rtypes.get(p));
335       rtypes.set(p, above);
336       initGuiWith(currentservice);
337       rdata.setSelectedIndex(p - 1);
338     }
339   }
340
341   @Override
342   protected void rdataRem_actionPerformed(ActionEvent e)
343   {
344     if (rdata.getSelectedIndex() > -1)
345     {
346       currentservice.getResultDataTypes().remove(rdata.getSelectedIndex());
347       initGuiWith(currentservice);
348     }
349   }
350
351   private boolean updateServiceFromGui()
352   {
353     Map<String, InputType> inputTypes = new HashMap<String, InputType>();
354     StringBuffer warnings = new StringBuffer();
355     for (String its : _iparam)
356     {
357       Matcher mtch = Pattern.compile("(\\S+)\\s(\\S+):\\[(.+)]").matcher(
358               its);
359       if (mtch.find())
360       {
361         if (!RestServiceDescription.parseTypeString(mtch.group(2) + ":"
362                 + mtch.group(3), mtch.group(1), mtch.group(2),
363                 mtch.group(3), inputTypes, warnings))
364         {
365           System.err
366                   .println("IMPLEMENTATION PROBLEM: Cannot parse RestService input parameter string '"
367                           + its + "'" + "\n" + warnings);
368         }
369       }
370     }
371     char gc = gapChar.getSelectedItem() == null ? ' ' : ((String) gapChar
372             .getSelectedItem()).charAt(0);
373     RestServiceDescription newService = new RestServiceDescription(
374             (String) action.getSelectedItem(), descr.getText().trim(), name
375                     .getText().trim(), url.getText().trim(), urlsuff
376                     .getText().trim(), inputTypes, hSeparable.isSelected(),
377             vSeparable.isSelected(), gc);
378
379     if (newService.isValid())
380     {
381       for (String its : _rparam)
382       {
383         JvDataType dtype;
384         try
385         {
386           dtype = JvDataType.valueOf(its);
387           newService.addResultDatatype(dtype);
388         } catch (Throwable x)
389         {
390
391           System.err
392                   .println("IMPLEMENTATION PROBLEM: Cannot parse RestService output parameter string '"
393                           + its + "'" + "\n" + warnings);
394         }
395       }
396       currentservice = newService;
397       return true;
398     }
399     else
400     {
401       System.err
402               .println("IMPLEMENTATION PROBLEM: Restservice generated from GUI is invalid\n"
403                       + warnings);
404
405     }
406     return false;
407   }
408
409   protected void refreshCutnPaste(boolean reparse)
410   {
411     if (!reparse && currentservice.isValid())
412     {
413       urldesc.setText(currentservice.toString());
414       parseWarnings.setVisible(false);
415     }
416     else
417     {
418       if (reparse)
419       {
420         String txt = urldesc.getText().trim();
421         if (txt.length() > 0)
422         {
423           RestServiceDescription rsd = null;
424           try
425           {
426             rsd = new RestServiceDescription(txt);
427             if (rsd.isValid())
428             {
429               parseWarnings.setVisible(false);
430               parseRes.setText("");
431               initGuiWith(currentservice = rsd);
432             }
433             else
434             {
435               parseRes.setText(MessageManager
436                       .formatMessage(
437                               "label.parsing_failed_syntax_errors_shown_below_param",
438                               new String[] { rsd.getInvalidMessage() }));
439               parseWarnings.setVisible(true);
440             }
441           } catch (Throwable e)
442           {
443             e.printStackTrace();
444             parseRes.setText(MessageManager
445                     .formatMessage(
446                             "label.parsing_failed_unrecoverable_exception_thrown_param",
447                             new String[] { e.toString() }));
448             parseWarnings.setVisible(true);
449           }
450         }
451         paste.revalidate();
452       }
453     }
454
455   }
456
457   public static void main(String[] args)
458   {
459     if (args.length == 0)
460     {
461       new Thread(new Runnable()
462       {
463         boolean visible = true;
464
465         public void run()
466         {
467           boolean nulserv = true;
468           while (visible)
469           {
470             final Thread runner = Thread.currentThread();
471             JFrame df = new JFrame();
472             df.getContentPane().setLayout(new BorderLayout());
473             df.getContentPane().add(
474                     (nulserv = !nulserv) ? new RestServiceEditorPane(
475                             jalview.ws.rest.RestClient
476                                     .makeShmmrRestClient()
477                                     .getRestDescription())
478                             : new RestServiceEditorPane(),
479                     BorderLayout.CENTER);
480             df.setBounds(100, 100, 600, 400);
481             df.addComponentListener(new ComponentListener()
482             {
483
484               @Override
485               public void componentShown(ComponentEvent e)
486               {
487
488               }
489
490               @Override
491               public void componentResized(ComponentEvent e)
492               {
493
494               }
495
496               @Override
497               public void componentMoved(ComponentEvent e)
498               {
499
500               }
501
502               @Override
503               public void componentHidden(ComponentEvent e)
504               {
505                 visible = false;
506                 runner.interrupt();
507
508               }
509             });
510             df.setVisible(true);
511             while (visible)
512             {
513               try
514               {
515                 Thread.sleep(10000);
516               } catch (Exception x)
517               {
518               }
519               ;
520             }
521             visible = true;
522           }
523         }
524       }).start();
525
526     }
527   }
528
529   String finalService = null;
530
531   public void showDialog(String title)
532   {
533     if (oldservice != null)
534     {
535       finalService = oldservice.toString();
536     }
537     JalviewDialog jvd = new JalviewDialog()
538     {
539
540       @Override
541       protected void raiseClosed()
542       {
543         // TODO Auto-generated method stub
544
545       }
546
547       @Override
548       protected void okPressed()
549       {
550         updateServiceFromGui();
551         finalService = currentservice.toString();
552       }
553
554       @Override
555       protected void cancelPressed()
556       {
557
558       }
559     };
560     JPanel pane = new JPanel(new BorderLayout()), okcancel = new JPanel(
561             new FlowLayout());
562     pane.add(this, BorderLayout.CENTER);
563     okcancel.add(jvd.ok);
564     okcancel.add(jvd.cancel);
565     pane.add(okcancel, BorderLayout.SOUTH);
566     jvd.initDialogFrame(pane, true, true, title, 600, 350);
567     jvd.waitForInput();
568   }
569
570   public String getEditedRestService()
571   {
572     return finalService;
573   }
574 }