JAL-3878 Make interactive service arguments persistent between starts.
[jalview.git] / src / jalview / ws2 / operations / AnnotationOperation.java
1 package jalview.ws2.operations;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.List;
6 import java.util.concurrent.CompletionStage;
7
8 import javax.swing.JCheckBoxMenuItem;
9 import javax.swing.JMenu;
10 import javax.swing.JMenuItem;
11 import javax.swing.event.MenuEvent;
12 import javax.swing.event.MenuListener;
13
14 import jalview.api.AlignCalcManagerI2;
15 import jalview.api.AlignmentViewPanel;
16 import jalview.datamodel.AlignmentAnnotation;
17 import jalview.gui.AlignFrame;
18 import jalview.gui.AlignViewport;
19 import jalview.gui.WsJobParameters;
20 import jalview.io.FeaturesFile;
21 import jalview.util.Format;
22 import jalview.util.MessageManager;
23 import jalview.ws.params.ArgumentI;
24 import jalview.ws.params.WsParamSetI;
25 import jalview.ws2.MenuEntryProviderI;
26 import jalview.ws2.ResultSupplier;
27 import jalview.ws2.PollingTaskExecutor;
28 import jalview.ws2.WebServiceI;
29
30 /**
31  *
32  * @author mmwarowny
33  *
34  */
35 public class AnnotationOperation implements Operation
36 {
37   final WebServiceI service;
38
39   final String typeName;
40
41   final ResultSupplier<List<AlignmentAnnotation>> annotationSupplier;
42
43   final ResultSupplier<FeaturesFile> featuresSupplier;
44
45   boolean alignmentAnalysis = false;
46
47   boolean interactive = false;
48
49   /*
50    * Is it fine to get rid of AlignAnalysisUIText?
51    */
52
53   public AnnotationOperation(WebServiceI service,
54       ResultSupplier<List<AlignmentAnnotation>> annotSupplier,
55       ResultSupplier<FeaturesFile> featSupplier, String operationName)
56   {
57     this.service = service;
58     this.annotationSupplier = annotSupplier;
59     this.featuresSupplier = featSupplier;
60     this.typeName = operationName;
61   }
62
63   @Override
64   public String getName()
65   {
66     return service.getName();
67   }
68
69   @Override
70   public String getTypeName()
71   {
72     return typeName;
73   }
74
75   @Override
76   public String getHostName()
77   {
78     return service.getHostName();
79   }
80
81   @Override
82   public int getMinSequences()
83   {
84     return 0;
85   }
86
87   @Override
88   public int getMaxSequences()
89   {
90     return Integer.MAX_VALUE;
91   }
92
93   @Override
94   public boolean canSubmitGaps()
95   {
96     return false;
97   }
98
99   @Override
100   public boolean isProteinOperation()
101   {
102     return true;
103   }
104
105   @Override
106   public boolean isNucleotideOperation()
107   {
108     return true;
109   }
110
111   @Override
112   public boolean isInteractive()
113   {
114     return interactive;
115   }
116
117   public void setInteractive(boolean value)
118   {
119     this.interactive = value;
120   }
121
122   @Override
123   public boolean isAlignmentAnalysis()
124   {
125     return alignmentAnalysis;
126   }
127
128   public void setAlignmentAnalysis(boolean value)
129   {
130     this.alignmentAnalysis = value;
131   }
132
133   @Override
134   public boolean getFilterNonStandardSymbols()
135   {
136     return false;
137   }
138
139   @Override
140   public boolean getNeedsAlignedSequences()
141   {
142     return false;
143   }
144
145   @Override
146   public MenuEntryProviderI getMenuBuilder()
147   {
148     if (isInteractive())
149       return this::buildInteractiveMenu;
150     else
151       return this::buildClassicMenu;
152   }
153
154   protected void buildClassicMenu(JMenu parent, AlignFrame frame)
155   {
156     final var calcName = service.getName();
157     PollingTaskExecutor wsExecutor = frame.getViewport().getWSExecutor();
158     final var calcManager = frame.getViewport().getCalcManager();
159     {
160       var item = new JMenuItem(MessageManager.formatMessage(
161           "label.calcname_with_default_settings", calcName));
162       item.addActionListener((event) -> {
163         var worker = createWorker(Collections.emptyList(), frame, calcManager);
164         calcManager.startWorker(worker);
165       });
166       parent.add(item);
167     }
168     if (service.hasParameters())
169     {
170       var item = new JMenuItem(
171           MessageManager.getString("label.edit_settings_and_run"));
172       item.setToolTipText(MessageManager.getString(
173           "label.view_and_change_parameters_before_running_calculation"));
174       item.addActionListener((event) -> {
175         openEditParamsDialog(service, null, null)
176             .thenAcceptAsync((arguments) -> {
177               if (arguments != null)
178               {
179                 var worker = createWorker(arguments, frame, calcManager);
180                 calcManager.startWorker(worker);
181               }
182             });
183       });
184       parent.add(item);
185     }
186   }
187
188   protected void buildInteractiveMenu(JMenu parent, AlignFrame frame)
189   {
190     final var calcName = service.getName();
191     final var calcManager = frame.getViewport().getCalcManager();
192     final var arguments = new ArrayList<ArgumentI>();
193     final JCheckBoxMenuItem runItem;
194     {
195       // TODO use MessageManager and set tool tip text
196       runItem = new JCheckBoxMenuItem(
197           String.format("%s calculations", calcName));
198       runItem.addActionListener((event) -> {
199         calcManager.removeWorkersForName(calcName);
200         var worker = createWorker(arguments, frame, calcManager);
201         calcManager.registerWorker(worker);
202       });
203       parent.add(runItem);
204     }
205     JMenuItem _editItem = null;
206     if (service.hasParameters())
207     {
208       // TODO use MessageManager and set tool tip text
209       _editItem = new JMenuItem(
210           String.format("Edit %s settings", calcName));
211       _editItem.addActionListener((event) -> {
212         openEditParamsDialog(service, null, null)
213             .thenAcceptAsync((args) -> {
214               if (arguments != null)
215               {
216                 arguments.clear();
217                 arguments.addAll(args);
218                 calcManager.removeWorkersForName(calcName);
219                 var worker = createWorker(arguments, frame, calcManager);
220                 calcManager.registerWorker(worker);
221               }
222             });
223       });
224       parent.add(_editItem);
225     }
226     final var editItem = _editItem;
227
228     parent.addMenuListener(new MenuListener()
229       {
230         @Override
231         public void menuSelected(MenuEvent e)
232         {
233           var isNuc = frame.getViewport().getAlignment().isNucleotide();
234           var menuEnabled = (isNuc && isNucleotideOperation()) ||
235               (!isNuc && isProteinOperation());
236           runItem.setEnabled(menuEnabled);
237           if (editItem != null)
238             editItem.setEnabled(menuEnabled);
239           boolean currentlyRunning = calcManager.getWorkersForName(calcName).size() > 0;
240           runItem.setSelected(currentlyRunning);
241         }
242
243         @Override
244         public void menuDeselected(MenuEvent e) {}
245
246         @Override
247         public void menuCanceled(MenuEvent e) {}
248       });
249   }
250
251   private AnnotationServiceWorker createWorker(
252       List<ArgumentI> arguments, AlignFrame frame, AlignCalcManagerI2 calcManager)
253   {
254     /* What is the purpose of AlignViewport and AlignmentViewPanel? */
255     AlignViewport viewport = frame.getCurrentView();
256     AlignmentViewPanel alignPanel = frame.alignPanel;
257     return new AnnotationServiceWorker(this, service,
258         arguments, viewport, alignPanel, frame, frame,
259         calcManager);
260   }
261
262   private CompletionStage<List<ArgumentI>> openEditParamsDialog(
263       WebServiceI service, WsParamSetI preset, List<ArgumentI> arguments)
264   {
265     WsJobParameters jobParams;
266     if (preset == null && arguments != null && arguments.size() > 0)
267       jobParams = new WsJobParameters(service.getParamStore(), preset,
268           arguments);
269     else
270       jobParams = new WsJobParameters(service.getParamStore(), preset,
271           null);
272     if (preset != null)
273     {
274       jobParams.setName(MessageManager.getString(
275           "label.adjusting_parameters_for_calculation"));
276     }
277     var stage = jobParams.showRunDialog();
278     return stage.thenApply((startJob) -> {
279       if (startJob)
280       {
281         if (jobParams.getPreset() == null)
282           return jobParams.getJobParams();
283         else
284           return jobParams.getPreset().getArguments();
285       }
286       else
287       {
288         return null;
289       }
290     });
291   }
292
293 }