JAL-3253 temporary branch SwingJS upgrade with testNG fixes Java 8
[jalview.git] / src / jalview / workers / AlignCalcManager.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.workers;
22
23 import jalview.api.AlignCalcManagerI;
24 import jalview.api.AlignCalcWorkerI;
25 import jalview.bin.Jalview;
26 import jalview.datamodel.AlignmentAnnotation;
27
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.HashSet;
31 import java.util.Hashtable;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Set;
35
36 public class AlignCalcManager implements AlignCalcManagerI
37 {
38   public static final String STARTED = "started";
39
40   public static final String FINISHED = "finished";
41
42   /*
43    * list of registered workers
44    */
45   private volatile List<AlignCalcWorkerI> restartable;
46
47   /*
48    * types of worker _not_ to run (for example, because they have
49    * previously thrown errors)
50    */
51   private volatile List<Class<? extends AlignCalcWorkerI>> blackList;
52
53   /*
54    * global record of calculations in progress
55    */
56   private volatile List<AlignCalcWorkerI> inProgress;
57
58   /*
59    * record of calculations pending or in progress in the current context
60    */
61   private volatile Map<Class<? extends AlignCalcWorkerI>, List<AlignCalcWorkerI>> updating;
62
63   /*
64    * workers that have run to completion so are candidates for visual-only 
65    * update of their results
66    */
67   private HashSet<AlignCalcWorkerI> canUpdate;
68
69   /**
70    * Constructor
71    */
72   public AlignCalcManager()
73   {
74     restartable = Collections
75             .synchronizedList(new ArrayList<AlignCalcWorkerI>());
76     blackList = Collections.synchronizedList(
77             new ArrayList<Class<? extends AlignCalcWorkerI>>());
78     inProgress = Collections
79             .synchronizedList(new ArrayList<AlignCalcWorkerI>());
80     updating = Collections.synchronizedMap(
81             new Hashtable<Class<? extends AlignCalcWorkerI>, List<AlignCalcWorkerI>>());
82     canUpdate = new HashSet<>();
83   }
84
85   @Override
86   public void notifyStart(AlignCalcWorkerI worker)
87   {
88     synchronized (updating)
89     {
90       Jalview.getInstance().notifyWorker(worker, STARTED);
91       List<AlignCalcWorkerI> upd = updating.get(worker.getClass());
92       if (upd == null)
93       {
94         updating.put(worker.getClass(), upd = Collections
95                 .synchronizedList(new ArrayList<AlignCalcWorkerI>()));
96       }
97       synchronized (upd)
98       {
99         upd.add(worker);
100       }
101     }
102   }
103
104   /*
105    * (non-Javadoc)
106    * 
107    * @see jalview.api.AlignCalcManagerI#isPending(jalview.api.AlignCalcWorkerI)
108    */
109   @Override
110   public boolean isPending(AlignCalcWorkerI workingClass)
111   {
112     List<AlignCalcWorkerI> upd;
113     synchronized (updating)
114     {
115       upd = updating.get(workingClass.getClass());
116       if (upd == null)
117       {
118         return false;
119       }
120       synchronized (upd)
121       {
122         if (upd.size() > 1)
123         {
124           return true;
125         }
126       }
127       return false;
128     }
129   }
130
131   @Override
132   public boolean notifyWorking(AlignCalcWorkerI worker)
133   {
134     synchronized (inProgress)
135     {
136       if (inProgress.contains(worker))
137       {
138         return false; // worker is already working, so ask caller to wait around
139       }
140       else
141       {
142         inProgress.add(worker);
143       }
144     }
145     return true;
146   }
147
148   @Override
149   public void workerComplete(AlignCalcWorkerI worker)
150   {
151     synchronized (inProgress)
152     {
153       Jalview.getInstance().notifyWorker(worker, FINISHED);
154       // System.err.println("Worker " + worker + " marked as complete.");
155       inProgress.remove(worker);
156       List<AlignCalcWorkerI> upd = updating.get(worker.getClass());
157       if (upd != null)
158       {
159         synchronized (upd)
160         {
161           upd.remove(worker);
162         }
163         canUpdate.add(worker);
164       }
165     }
166   }
167
168   @Override
169   public void disableWorker(AlignCalcWorkerI worker)
170   {
171     synchronized (blackList)
172     {
173       blackList.add(worker.getClass());
174     }
175   }
176
177   @Override
178   public boolean isDisabled(AlignCalcWorkerI worker)
179   {
180     synchronized (blackList)
181     {
182       return blackList.contains(worker.getClass());
183     }
184   }
185
186   @Override
187   public void startWorker(AlignCalcWorkerI worker)
188   {
189     if (!isDisabled(worker))
190     {
191       Thread tw = new Thread(worker);
192       tw.setName(worker.getClass().toString());
193       Jalview.execThread(tw); // JAL-3563
194       // tw.start();
195     }
196   }
197
198   @Override
199   public boolean isWorking(AlignCalcWorkerI worker)
200   {
201     synchronized (inProgress)
202     {// System.err.println("isWorking : worker "+(worker!=null ?
203      // worker.getClass():"null")+ " "+hashCode());
204       return worker != null && inProgress.contains(worker);
205     }
206   }
207
208   @Override
209   public boolean isWorking()
210   {
211     synchronized (inProgress)
212     {
213       // System.err.println("isWorking "+hashCode());
214       return inProgress.size() > 0;
215     }
216   }
217
218   @Override
219   public void registerWorker(AlignCalcWorkerI worker)
220   {
221     synchronized (restartable)
222     {
223       if (!restartable.contains(worker))
224       {
225         restartable.add(worker);
226       }
227       startWorker(worker);
228     }
229   }
230
231   @Override
232   public void restartWorkers()
233   {
234     synchronized (restartable)
235     {
236       for (AlignCalcWorkerI worker : restartable)
237       {
238         startWorker(worker);
239       }
240     }
241   }
242
243   @Override
244   public boolean workingInvolvedWith(
245           AlignmentAnnotation alignmentAnnotation)
246   {
247     synchronized (inProgress)
248     {
249       for (AlignCalcWorkerI worker : inProgress)
250       {
251         if (worker.involves(alignmentAnnotation))
252         {
253           return true;
254         }
255       }
256     }
257     synchronized (updating)
258     {
259       for (List<AlignCalcWorkerI> workers : updating.values())
260       {
261         for (AlignCalcWorkerI worker : workers)
262         {
263           if (worker.involves(alignmentAnnotation))
264           {
265             return true;
266           }
267         }
268       }
269     }
270     return false;
271   }
272
273   @Override
274   public void updateAnnotationFor(
275           Class<? extends AlignCalcWorkerI> workerClass)
276   {
277
278     AlignCalcWorkerI[] workers;
279     synchronized (canUpdate)
280     {
281       workers = canUpdate.toArray(new AlignCalcWorkerI[0]);
282     }
283     for (AlignCalcWorkerI worker : workers)
284     {
285       if (workerClass.equals(worker.getClass()))
286       {
287         worker.updateAnnotation();
288       }
289     }
290   }
291
292   @Override
293   public List<AlignCalcWorkerI> getRegisteredWorkersOfClass(
294           Class<? extends AlignCalcWorkerI> workerClass)
295   {
296     List<AlignCalcWorkerI> workingClass = new ArrayList<>();
297     synchronized (canUpdate)
298     {
299       for (AlignCalcWorkerI worker : canUpdate)
300       {
301         if (workerClass.equals(worker.getClass()))
302         {
303           workingClass.add(worker);
304         }
305       }
306     }
307     return (workingClass.size() == 0) ? null : workingClass;
308   }
309
310   @Override
311   public void enableWorker(AlignCalcWorkerI worker)
312   {
313     synchronized (blackList)
314     {
315       blackList.remove(worker.getClass());
316     }
317   }
318
319   @Override
320   public void removeRegisteredWorkersOfClass(
321           Class<? extends AlignCalcWorkerI> typeToRemove)
322   {
323     List<AlignCalcWorkerI> removable = new ArrayList<>();
324     Set<AlignCalcWorkerI> toremovannot = new HashSet<>();
325     synchronized (restartable)
326     {
327       for (AlignCalcWorkerI worker : restartable)
328       {
329         if (typeToRemove.equals(worker.getClass()))
330         {
331           removable.add(worker);
332           toremovannot.add(worker);
333         }
334       }
335       restartable.removeAll(removable);
336     }
337     synchronized (canUpdate)
338     {
339       for (AlignCalcWorkerI worker : canUpdate)
340       {
341         if (typeToRemove.equals(worker.getClass()))
342         {
343           removable.add(worker);
344           toremovannot.add(worker);
345         }
346       }
347       canUpdate.removeAll(removable);
348     }
349     // TODO: finish testing this extension
350
351     /*
352      * synchronized (inProgress) { // need to kill or mark as dead any running
353      * threads... (inProgress.get(typeToRemove)); }
354      * 
355      * if (workers == null) { return; } for (AlignCalcWorkerI worker : workers)
356      * {
357      * 
358      * if (isPending(worker)) { worker.abortAndDestroy(); startWorker(worker); }
359      * else { System.err.println("Pending exists for " + workerClass); } }
360      */
361   }
362
363   /**
364    * Deletes the worker that update the given annotation, provided it is marked
365    * as deletable.
366    */
367   @Override
368   public void removeWorkerForAnnotation(AlignmentAnnotation ann)
369   {
370     /*
371      * first just find those to remove (to avoid
372      * ConcurrentModificationException)
373      */
374     List<AlignCalcWorkerI> toRemove = new ArrayList<>();
375     for (AlignCalcWorkerI worker : restartable)
376     {
377       if (worker.involves(ann))
378       {
379         if (worker.isDeletable())
380         {
381           toRemove.add(worker);
382         }
383       }
384     }
385
386     /*
387      * remove all references to deleted workers so any references 
388      * they hold to annotation data can be garbage collected 
389      */
390     for (AlignCalcWorkerI worker : toRemove)
391     {
392       restartable.remove(worker);
393       blackList.remove(worker.getClass());
394       inProgress.remove(worker);
395       canUpdate.remove(worker);
396       synchronized (updating)
397       {
398         List<AlignCalcWorkerI> upd = updating.get(worker.getClass());
399         if (upd != null)
400         {
401           upd.remove(worker);
402         }
403       }
404     }
405   }
406 }