JAL-1807 explicit imports (jalview.ws.*)
[jalview.git] / src / jalview / ws / rest / RestClient.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.ws.rest;
22
23 import jalview.bin.Cache;
24 import jalview.datamodel.AlignmentView;
25 import jalview.gui.AlignFrame;
26 import jalview.gui.AlignViewport;
27 import jalview.gui.AlignmentPanel;
28 import jalview.gui.Desktop;
29 import jalview.gui.WebserviceInfo;
30 import jalview.io.packed.DataProvider.JvDataType;
31 import jalview.io.packed.JalviewDataset;
32 import jalview.util.MessageManager;
33 import jalview.ws.WSClient;
34 import jalview.ws.WSClientI;
35 import jalview.ws.WSMenuEntryProviderI;
36 import jalview.ws.rest.params.Alignment;
37 import jalview.ws.rest.params.JobConstant;
38 import jalview.ws.rest.params.SeqGroupIndexVector;
39
40 import java.awt.event.ActionEvent;
41 import java.awt.event.ActionListener;
42 import java.util.Hashtable;
43 import java.util.Vector;
44
45 import javax.swing.JMenu;
46 import javax.swing.JMenuItem;
47 import javax.swing.JOptionPane;
48 import javax.swing.event.MenuEvent;
49 import javax.swing.event.MenuListener;
50
51 /**
52  * @author JimP
53  * 
54  */
55 public class RestClient extends WSClient implements WSClientI,
56         WSMenuEntryProviderI
57 {
58   RestServiceDescription service;
59
60   public RestClient(RestServiceDescription rsd)
61   {
62     service = rsd;
63   }
64
65   /**
66    * parent alignframe for this job
67    */
68   AlignFrame af;
69
70   /**
71    * alignment view which provides data for job.
72    */
73   AlignViewport av;
74
75   /**
76    * get the alignFrame for the associated input data if it exists.
77    * 
78    * @return
79    */
80   protected AlignFrame recoverAlignFrameForView()
81   {
82     return Desktop.getAlignFrameFor(av);
83   }
84
85   public RestClient(RestServiceDescription service2, AlignFrame alignFrame)
86   {
87     this(service2, alignFrame, false);
88   }
89
90   boolean headless = false;
91
92   public RestClient(RestServiceDescription service2, AlignFrame alignFrame,
93           boolean nogui)
94   {
95     service = service2;
96     af = alignFrame;
97     av = alignFrame.getViewport();
98     headless = nogui;
99     constructJob();
100   }
101
102   public void setWebserviceInfo(boolean headless)
103   {
104     WebServiceJobTitle = MessageManager.formatMessage("label.webservice_job_title", new String[]{service.details.Action,service.details.Name});
105     WebServiceName = service.details.Name;
106     WebServiceReference = "No reference - go to url for more info";
107     if (service.details.description != null)
108     {
109       WebServiceReference = service.details.description;
110     }
111     if (!headless)
112     {
113       wsInfo = new WebserviceInfo(WebServiceJobTitle, WebServiceName + "\n"
114               + WebServiceReference, true);
115       wsInfo.setRenderAsHtml(true);
116     }
117
118   }
119
120   @Override
121   public boolean isCancellable()
122   {
123     // TODO define process for cancelling rsbws jobs
124     return false;
125   }
126
127   @Override
128   public boolean canMergeResults()
129   {
130     // TODO process service definition to identify if the results might be
131     // mergeable
132     // TODO: change comparison for annotation merge
133     return false;
134   }
135
136   @Override
137   public void cancelJob()
138   {
139     System.err.println("Cannot cancel this job type: " + service);
140   }
141
142   @Override
143   public void attachWSMenuEntry(final JMenu wsmenu,
144           final AlignFrame alignFrame)
145   {
146     JMenuItem submit = new JMenuItem(service.details.Name);
147     submit.setToolTipText(MessageManager.formatMessage("label.rest_client_submit", new String[]{service.details.Action,service.details.Name}));
148     submit.addActionListener(new ActionListener()
149     {
150
151       @Override
152       public void actionPerformed(ActionEvent e)
153       {
154         new RestClient(service, alignFrame);
155       }
156
157     });
158     wsmenu.add(submit);
159     // TODO: menu listener should enable/disable entry depending upon selection
160     // state of the alignment
161     wsmenu.addMenuListener(new MenuListener()
162     {
163
164       @Override
165       public void menuSelected(MenuEvent e)
166       {
167         // TODO Auto-generated method stub
168
169       }
170
171       @Override
172       public void menuDeselected(MenuEvent e)
173       {
174         // TODO Auto-generated method stub
175
176       }
177
178       @Override
179       public void menuCanceled(MenuEvent e)
180       {
181         // TODO Auto-generated method stub
182
183       }
184
185     });
186
187   }
188
189   /**
190    * record of initial undoredo hash for the alignFrame providing data for this
191    * job.
192    */
193   long[] undoredo = null;
194
195   /**
196    * Compare the original input data to the data currently presented to the
197    * user. // LOGIC: compare undo/redo - if same, merge regardless (coping with
198    * any changes in hidden columns as normal) // if different undo/redo then
199    * compare region that was submitted // if same, then merge as before, if
200    * different then prompt user to open a new window.
201    * 
202    * @return
203    */
204   protected boolean isAlignmentModified()
205   {
206     if (undoredo == null || av == null || av.getAlignment() == null)
207     {
208       // always return modified if we don't have access to live GUI elements
209       // anymore.
210       return true;
211     }
212     if (av.isUndoRedoHashModified(undoredo))
213     {
214       // alignment has been modified in some way.
215       return true;
216     }
217     // TODO: look deeper into modification of selection state, etc that may
218     // affect RestJobThread.realiseResults(boolean merge);
219     return false;
220
221   }
222
223   /**
224    * TODO: combine to form a dataset+alignment+annotation context
225    */
226   AlignmentView _input;
227
228   /**
229    * input data context
230    */
231   JalviewDataset jds;
232
233   /**
234    * informative name for results
235    */
236   public String viewTitle;
237
238   protected void constructJob()
239   {
240     service.setInvolvesFlags();
241     // record all aspects of alignment view so we can merge back or recreate
242     // later
243     undoredo = av.getUndoRedoHash();
244     /**
245      * delete ? Vector sgs = av.getAlignment().getGroups(); if (sgs!=null) {
246      * _sgs = new SequenceGroup[sgs.size()]; sgs.copyInto(_sgs); } else { _sgs =
247      * new SequenceGroup[0]; }
248      */
249     boolean selExists = (av.getSelectionGroup() != null)
250             && (av.getSelectionGroup().getSize() > 1);
251     // TODO: JAL-715: refactor to alignViewport methods and revise to full
252     // focus+context+dataset input data staging model
253     if (selExists)
254     {
255       if (service.partitiondata)
256       {
257         if (av.getAlignment().getGroups() != null
258                 && av.getAlignment().getGroups().size() > 0)
259         {
260           // intersect groups with selected region
261           _input = new AlignmentView(av.getAlignment(),
262                   av.getColumnSelection(), av.getSelectionGroup(),
263                   av.hasHiddenColumns(), true, true);
264           viewTitle = MessageManager.formatMessage("label.select_visible_region_of", new String[]{(av.hasHiddenColumns() ? MessageManager.getString("label.visible") : ""),af.getTitle()});
265         }
266         else
267         {
268           // use selected region to partition alignment
269           _input = new AlignmentView(av.getAlignment(),
270                   av.getColumnSelection(), av.getSelectionGroup(),
271                   av.hasHiddenColumns(), false, true);
272         }
273         viewTitle = MessageManager.formatMessage("label.select_unselect_visible_regions_from", new String[]{(av.hasHiddenColumns() ? MessageManager.getString("label.visible") : ""),af.getTitle()});
274       }
275       else
276       {
277         // just take selected region intersection
278         _input = new AlignmentView(av.getAlignment(),
279                 av.getColumnSelection(), av.getSelectionGroup(),
280                 av.hasHiddenColumns(), true, true);
281         viewTitle = MessageManager.formatMessage("label.select_visible_region_of", new String[]{(av.hasHiddenColumns() ? MessageManager.getString("label.visible") : ""),af.getTitle()});
282       }
283     }
284     else
285     {
286       // standard alignment view without selection present
287       _input = new AlignmentView(av.getAlignment(),
288               av.getColumnSelection(), null, av.hasHiddenColumns(), false,
289               true);
290       viewTitle = "" + (av.hasHiddenColumns() ? (new StringBuffer(" ").append(MessageManager.getString("label.visible_region_of")).toString()) : "")
291               + af.getTitle();
292     }
293
294     RestJobThread jobsthread = new RestJobThread(this);
295
296     if (jobsthread.isValid())
297     {
298       setWebserviceInfo(headless);
299       if (!headless)
300       {
301         wsInfo.setthisService(this);
302         jobsthread.setWebServiceInfo(wsInfo);
303       }
304       jobsthread.start();
305     }
306     else
307     {
308       // TODO: try to tell the user why the job couldn't be started.
309       JOptionPane
310               .showMessageDialog(
311                       Desktop.desktop,
312                       (jobsthread.hasWarnings() ? jobsthread.getWarnings()
313                               : MessageManager.getString("label.job_couldnt_be_started_check_input")),
314                       MessageManager.getString("label.unable_start_web_service_analysis"),
315                       JOptionPane.WARNING_MESSAGE);
316     }
317   }
318
319   public static RestClient makeShmmrRestClient()
320   {
321     String action = "Analysis", description = "Sequence Harmony and Multi-Relief (Brandt et al. 2010)", name = MessageManager.getString("label.multiharmony");
322     Hashtable<String, InputType> iparams = new Hashtable<String, InputType>();
323     JobConstant toolp;
324     // toolp = new jalview.ws.rest.JobConstant("tool","jalview");
325     // iparams.put(toolp.token, toolp);
326     // toolp = new jalview.ws.rest.params.JobConstant("mbjob[method]","shmr");
327     // iparams.put(toolp.token, toolp);
328     // toolp = new
329     // jalview.ws.rest.params.JobConstant("mbjob[description]","step 1");
330     // iparams.put(toolp.token, toolp);
331     // toolp = new jalview.ws.rest.params.JobConstant("start_search","1");
332     // iparams.put(toolp.token, toolp);
333     // toolp = new jalview.ws.rest.params.JobConstant("blast","0");
334     // iparams.put(toolp.token, toolp);
335
336     Alignment aliinput = new Alignment();
337     // SHMR server has a 65K limit for content pasted into the 'ali' parameter,
338     // so we always upload our files.
339     aliinput.token = "ali_file";
340     aliinput.writeAsFile = true;
341     iparams.put(aliinput.token, aliinput);
342     SeqGroupIndexVector sgroups = new SeqGroupIndexVector();
343     sgroups.setMinsize(2);
344     sgroups.min = 2;// need at least two group defined to make a partition
345     iparams.put("groups", sgroups);
346     sgroups.token = "groups";
347     sgroups.sep = " ";
348     RestServiceDescription shmrService = new RestServiceDescription(
349             action,
350             description,
351             name,
352             "http://zeus.few.vu.nl/programs/shmrwww/index.php?tool=jalview",// ?tool=jalview&mbjob[method]=shmr&mbjob[description]=step1",
353             "?tool=jalview", iparams, true, false, '-');
354     // a priori knowledge of the data returned from the service
355     shmrService.addResultDatatype(JvDataType.ANNOTATION);
356     return new RestClient(shmrService);
357   }
358
359   public AlignmentPanel recoverAlignPanelForView()
360   {
361     AlignmentPanel[] aps = Desktop
362             .getAlignmentPanels(av.getSequenceSetId());
363     for (AlignmentPanel alp : aps)
364     {
365       if (alp.av == av)
366       {
367         return alp;
368       }
369     }
370     return null;
371   }
372
373   public boolean isShowResultsInNewView()
374   {
375     // TODO make this a property of the service
376     return true;
377   }
378
379   protected static Vector<String> services = null;
380
381   public static final String RSBS_SERVICES = "RSBS_SERVICES";
382
383   public static RestClient[] getRestClients()
384   {
385     if (services == null)
386     {
387       services = new Vector<String>();
388       try
389       {
390         for (RestServiceDescription descr : RestServiceDescription
391                 .parseDescriptions(Cache.getDefault(
392                         RSBS_SERVICES,
393                         makeShmmrRestClient().service.toString())))
394         {
395           services.add(descr.toString());
396         }
397       } catch (Exception ex)
398       {
399         System.err
400                 .println("Serious - RSBS descriptions in user preferences are corrupt!");
401         ex.printStackTrace();
402       }
403
404     }
405     RestClient[] lst = new RestClient[services.size()];
406     int i = 0;
407     for (String svc : services)
408     {
409       lst[i++] = new RestClient(new RestServiceDescription(svc));
410     }
411     return lst;
412   }
413
414   public String getAction()
415   {
416     return service.details.Action;
417   }
418
419   public RestServiceDescription getRestDescription()
420   {
421     return service;
422   }
423
424   public static Vector<String> getRsbsDescriptions()
425   {
426     Vector<String> rsbsDescrs = new Vector<String>();
427     for (RestClient rsbs : getRestClients())
428     {
429       rsbsDescrs.add(rsbs.getRestDescription().toString());
430     }
431     return rsbsDescrs;
432   }
433
434   public static void setRsbsServices(Vector<String> rsbsUrls)
435   {
436     if (rsbsUrls != null)
437     {
438       // TODO: consider validating services ?
439       services = new Vector<String>(rsbsUrls);
440       StringBuffer sprop = new StringBuffer();
441       for (String s : services)
442       {
443         sprop.append(s);
444       }
445       Cache.setProperty(RSBS_SERVICES, sprop.toString());
446     }
447     else
448     {
449       Cache.removeProperty(RSBS_SERVICES);
450     }
451   }
452
453 }