JAL-4241 Fix annotation and feature alignment with selection
[jalview.git] / src / jalview / ws2 / actions / annotation / AlignCalcWorkerAdapter.java
1 package jalview.ws2.actions.annotation;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.List;
6
7 import jalview.analysis.AlignmentAnnotationUtils;
8 import jalview.api.AlignViewportI;
9 import jalview.api.AlignmentViewPanel;
10 import jalview.api.PollableAlignCalcWorkerI;
11 import jalview.datamodel.AlignmentAnnotation;
12 import jalview.datamodel.AlignmentI;
13 import jalview.workers.AlignCalcWorker;
14 import jalview.ws.params.ArgumentI;
15 import jalview.ws2.actions.api.TaskEventListener;
16 import jalview.ws2.actions.api.TaskI;
17 import jalview.ws2.api.Credentials;
18
19 public class AlignCalcWorkerAdapter extends AlignCalcWorker implements PollableAlignCalcWorkerI
20 {
21   private static int calcCount = 0;
22
23   private final int calcNumber = calcCount++;
24
25   private final AnnotationAction action;
26
27   private final List<ArgumentI> args;
28
29   private final Credentials credentials;
30
31   private TaskI<AnnotationResult> currentTask = null;
32
33   private TaskEventListener<AnnotationResult> taskListener = new TaskEventListener<>()
34   {
35     @Override
36     public void taskCompleted(TaskI source, AnnotationResult result)
37     {
38       int graphGroup = alignViewport.getAlignment().getLastGraphGroup();
39       List<AlignmentAnnotation> annotations = new ArrayList<>();
40       for (AlignmentAnnotation ala : result.getAnnotations())
41       {
42         if (ala.graphGroup > 0)
43           ala.graphGroup += graphGroup;
44         var newAnnot = alignViewport.getAlignment()
45             .updateFromOrCopyAnnotation(ala);
46         if (newAnnot.sequenceRef != null)
47         {
48           newAnnot.sequenceRef.addAlignmentAnnotation(newAnnot);
49           newAnnot.adjustForAlignment();
50           AlignmentAnnotationUtils.replaceAnnotationOnAlignmentWith(
51               newAnnot, newAnnot.label, newAnnot.getCalcId());
52         }
53         annotations.add(newAnnot);
54       }
55       updateOurAnnots(annotations);
56       listener.workerHasResult(
57           AlignCalcWorkerAdapter.this,
58           new AnnotationResult(
59               annotations,
60               result.hasFeatures,
61               result.featureColours,
62               result.featureFilters));
63     }
64   };
65
66   public AlignCalcWorkerAdapter(AlignViewportI alignViewport, AlignmentViewPanel alignPanel, AnnotationAction action,
67       List<ArgumentI> args, Credentials credentials)
68   {
69     super(alignViewport, alignPanel);
70     this.action = action;
71     this.args = args;
72     this.credentials = credentials;
73   }
74
75   @Override
76   public String getCalcName()
77   {
78     return action.getWebService().getName();
79   }
80
81   @Override
82   public void updateAnnotation()
83   {
84
85   }
86
87   private void updateOurAnnots(List<AlignmentAnnotation> newAnnots)
88   {
89     List<AlignmentAnnotation> oldAnnots = ourAnnots != null ? ourAnnots : List.of();
90     AlignmentI alignment = alignViewport.getAlignment();
91     for (AlignmentAnnotation annotation : oldAnnots)
92       if (!newAnnots.contains(annotation))
93         alignment.deleteAnnotation(annotation);
94     for (AlignmentAnnotation annotation : newAnnots)
95       alignment.validateAnnotation(annotation);
96     ourAnnots = Collections.synchronizedList(newAnnots);
97   }
98
99   @Override
100   public synchronized void startUp() throws Throwable
101   {
102     if (alignViewport.isClosed())
103     {
104       calcMan.disableWorker(this);
105       abortAndDestroy();
106       throw new IllegalStateException("Starting calculation for closed viewport");
107     }
108     currentTask = action.createTask(alignViewport, args, credentials);
109     currentTask.addTaskEventListener(taskListener);
110     currentTask.init();
111     listener.workerStarted(this);
112   }
113
114   @Override
115   public boolean poll() throws Throwable
116   {
117     return currentTask.poll();
118   }
119
120   @Override
121   public synchronized void cancel()
122   {
123     try
124     {
125       currentTask.cancel();
126     } finally
127     {
128       currentTask.removeTaskEventListener(taskListener);
129       listener.workerStopped(this);
130     }
131   }
132
133   @Override
134   public synchronized void done()
135   {
136     try
137     {
138       currentTask.complete();
139     } catch (Exception e)
140     {
141     } finally
142     {
143       currentTask.removeTaskEventListener(taskListener);
144       listener.workerStopped(this);
145     }
146   }
147
148   @Override
149   public boolean isDeletable()
150   {
151     return true;
152   }
153
154   @Override
155   public String toString()
156   {
157     return "AlignCalcWorkerAdapter-" + calcNumber + " for " + getCalcName();
158   }
159
160   public interface WorkerListener extends java.util.EventListener
161   {
162     default void workerStarted(AlignCalcWorkerAdapter source)
163     {
164     };
165
166     default void workerStopped(AlignCalcWorkerAdapter source)
167     {
168     };
169
170     default void workerHasResult(AlignCalcWorkerAdapter source, AnnotationResult result)
171     {
172     };
173
174     static final WorkerListener NULL_LISTENER = new WorkerListener()
175     {
176     };
177   }
178
179   private WorkerListener listener = WorkerListener.NULL_LISTENER;
180
181   public void setWorkerListener(WorkerListener listener)
182   {
183     if (listener == null) listener = WorkerListener.NULL_LISTENER;
184     this.listener = listener;
185   }
186 }