JAL-1863 allow RNA secondary structure rows exported by Jalview to be imported again
[jalview.git] / src / 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[]
439                               { rsd.getInvalidMessage() }));
440               parseWarnings.setVisible(true);
441             }
442           } catch (Throwable e)
443           {
444             e.printStackTrace();
445             parseRes.setText(MessageManager
446                     .formatMessage(
447                             "label.parsing_failed_unrecoverable_exception_thrown_param",
448                             new String[]
449                             { e.toString() }));
450             parseWarnings.setVisible(true);
451           }
452         }
453         paste.revalidate();
454       }
455     }
456
457   }
458
459   public static void main(String[] args)
460   {
461     if (args.length == 0)
462     {
463       new Thread(new Runnable()
464       {
465         boolean visible = true;
466
467         public void run()
468         {
469           boolean nulserv = true;
470           while (visible)
471           {
472             final Thread runner = Thread.currentThread();
473             JFrame df = new JFrame();
474             df.getContentPane().setLayout(new BorderLayout());
475             df.getContentPane().add(
476                     (nulserv = !nulserv) ? new RestServiceEditorPane(
477                             jalview.ws.rest.RestClient
478                                     .makeShmmrRestClient()
479                                     .getRestDescription())
480                             : new RestServiceEditorPane(),
481                     BorderLayout.CENTER);
482             df.setBounds(100, 100, 600, 400);
483             df.addComponentListener(new ComponentListener()
484             {
485
486               @Override
487               public void componentShown(ComponentEvent e)
488               {
489
490               }
491
492               @Override
493               public void componentResized(ComponentEvent e)
494               {
495
496               }
497
498               @Override
499               public void componentMoved(ComponentEvent e)
500               {
501
502               }
503
504               @Override
505               public void componentHidden(ComponentEvent e)
506               {
507                 visible = false;
508                 runner.interrupt();
509
510               }
511             });
512             df.setVisible(true);
513             while (visible)
514             {
515               try
516               {
517                 Thread.sleep(10000);
518               } catch (Exception x)
519               {
520               }
521               ;
522             }
523             visible = true;
524           }
525         }
526       }).start();
527
528     }
529   }
530
531   String finalService = null;
532
533   public void showDialog(String title)
534   {
535     if (oldservice != null)
536     {
537       finalService = oldservice.toString();
538     }
539     JalviewDialog jvd = new JalviewDialog()
540     {
541
542       @Override
543       protected void raiseClosed()
544       {
545         // TODO Auto-generated method stub
546
547       }
548
549       @Override
550       protected void okPressed()
551       {
552         updateServiceFromGui();
553         finalService = currentservice.toString();
554       }
555
556       @Override
557       protected void cancelPressed()
558       {
559
560       }
561     };
562     JPanel pane = new JPanel(new BorderLayout()), okcancel = new JPanel(
563             new FlowLayout());
564     pane.add(this, BorderLayout.CENTER);
565     okcancel.add(jvd.ok);
566     okcancel.add(jvd.cancel);
567     pane.add(okcancel, BorderLayout.SOUTH);
568     jvd.initDialogFrame(pane, true, true, title, 600, 350);
569     jvd.waitForInput();
570   }
571
572   public String getEditedRestService()
573   {
574     return finalService;
575   }
576 }