JAL-1432 updated copyright notices
[jalview.git] / src / jalview / workers / AlignCalcManager.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.0b1)
3  * Copyright (C) 2014 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 of the License, or (at your option) any later version.
10  *  
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  * The Jalview Authors are detailed in the 'AUTHORS' file.
18  */
19 package jalview.workers;
20
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.HashSet;
24 import java.util.Hashtable;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Set;
28
29 import jalview.api.AlignCalcManagerI;
30 import jalview.api.AlignCalcWorkerI;
31 import jalview.datamodel.AlignmentAnnotation;
32
33 public class AlignCalcManager implements AlignCalcManagerI
34 {
35   private volatile List<AlignCalcWorkerI> restartable = Collections
36           .synchronizedList(new ArrayList<AlignCalcWorkerI>());
37
38   private volatile List<Class> blackList = Collections
39           .synchronizedList(new ArrayList<Class>());
40
41   /**
42    * global record of calculations in progress
43    */
44   private volatile Map<Class, AlignCalcWorkerI> inProgress = Collections
45           .synchronizedMap(new Hashtable<Class, AlignCalcWorkerI>());
46
47   /**
48    * record of calculations pending or in progress in the current context
49    */
50   private volatile Map<Class, List<AlignCalcWorkerI>> updating = Collections
51           .synchronizedMap(new Hashtable<Class, List<AlignCalcWorkerI>>());
52
53   @Override
54   public void notifyStart(AlignCalcWorkerI worker)
55   {
56     synchronized (updating)
57     {
58       List<AlignCalcWorkerI> upd = updating.get(worker.getClass());
59       if (upd == null)
60       {
61         updating.put(
62                 worker.getClass(),
63                 upd = Collections
64                         .synchronizedList(new ArrayList<AlignCalcWorkerI>()));
65       }
66       synchronized (upd)
67       {
68         upd.add(worker);
69       }
70     }
71   }
72
73   @Override
74   public boolean alreadyDoing(AlignCalcWorkerI worker)
75   {
76     synchronized (inProgress)
77     {
78       return inProgress.containsKey(worker.getClass());
79     }
80   }
81
82   /*
83    * (non-Javadoc)
84    * 
85    * @see jalview.api.AlignCalcManagerI#isPending(jalview.api.AlignCalcWorkerI)
86    */
87   @Override
88   public boolean isPending(AlignCalcWorkerI workingClass)
89   {
90     List<AlignCalcWorkerI> upd;
91     synchronized (updating)
92     {
93       upd = updating.get(workingClass.getClass());
94       if (upd == null)
95       {
96         return false;
97       }
98       synchronized (upd)
99       {
100         if (upd.size() > 1)
101         {
102           return true;
103         }
104       }
105       return false;
106     }
107   }
108
109   // TODO make into api method if needed ?
110   public int numberLive(AlignCalcWorkerI worker)
111   {
112     synchronized (updating)
113     {
114       List<AlignCalcWorkerI> upd = updating.get(worker.getClass());
115       if (upd == null)
116       {
117         return 0;
118       }
119       ;
120       return upd.size();
121     }
122   }
123
124   @Override
125   public boolean notifyWorking(AlignCalcWorkerI worker)
126   {
127     synchronized (inProgress)
128     {
129       // TODO: decide if we should throw exceptions here if multiple workers
130       // start to work
131       if (inProgress.get(worker.getClass()) != null)
132       {
133         if (false)
134         {
135           System.err
136                   .println("Warning: Multiple workers are running of type "
137                           + worker.getClass());
138         }
139         return false;
140       }
141       inProgress.put(worker.getClass(), worker);
142     }
143     return true;
144   }
145
146   private final HashSet<AlignCalcWorkerI> canUpdate = new HashSet<AlignCalcWorkerI>();
147
148   @Override
149   public void workerComplete(AlignCalcWorkerI worker)
150   {
151     synchronized (inProgress)
152     {
153       // System.err.println("Worker "+worker.getClass()+" marked as complete.");
154       inProgress.remove(worker.getClass());
155       List<AlignCalcWorkerI> upd = updating.get(worker.getClass());
156       if (upd != null)
157       {
158         synchronized (upd)
159         {
160           upd.remove(worker);
161         }
162         canUpdate.add(worker);
163       }
164     }
165   }
166
167   @Override
168   public void workerCannotRun(AlignCalcWorkerI worker)
169   {
170     synchronized (blackList)
171     {
172       blackList.add(worker.getClass());
173     }
174   }
175
176   public boolean isBlackListed(Class workerType)
177   {
178     synchronized (blackList)
179     {
180       return blackList.contains(workerType);
181     }
182   }
183
184   @Override
185   public void startWorker(AlignCalcWorkerI worker)
186   {
187     // System.err.println("Starting "+worker.getClass());
188     // new Exception("").printStackTrace();
189     Thread tw = new Thread(worker);
190     tw.setName(worker.getClass().toString());
191     tw.start();
192   }
193
194   @Override
195   public boolean isWorking(AlignCalcWorkerI worker)
196   {
197     synchronized (inProgress)
198     {// System.err.println("isWorking : worker "+(worker!=null ?
199      // worker.getClass():"null")+ " "+hashCode());
200       return worker != null && inProgress.get(worker.getClass()) == worker;
201     }
202   }
203
204   @Override
205   public boolean isWorking()
206   {
207     synchronized (inProgress)
208     {
209       // System.err.println("isWorking "+hashCode());
210       return inProgress.size() > 0;
211     }
212   }
213
214   @Override
215   public void registerWorker(AlignCalcWorkerI worker)
216   {
217     synchronized (restartable)
218     {
219       if (!restartable.contains(worker))
220       {
221         restartable.add(worker);
222       }
223       startWorker(worker);
224     }
225   }
226
227   @Override
228   public void restartWorkers()
229   {
230     synchronized (restartable)
231     {
232       for (AlignCalcWorkerI worker : restartable)
233       {
234         startWorker(worker);
235       }
236     }
237   }
238
239   @Override
240   public boolean workingInvolvedWith(AlignmentAnnotation alignmentAnnotation)
241   {
242     synchronized (inProgress)
243     {
244       for (AlignCalcWorkerI worker : inProgress.values())
245       {
246         if (worker.involves(alignmentAnnotation))
247         {
248           return true;
249         }
250       }
251     }
252     synchronized (updating)
253     {
254       for (List<AlignCalcWorkerI> workers : updating.values())
255       {
256         for (AlignCalcWorkerI worker : workers)
257           if (worker.involves(alignmentAnnotation))
258           {
259             return true;
260           }
261       }
262     }
263     return false;
264   }
265
266   @Override
267   public void updateAnnotationFor(Class workerClass)
268   {
269
270     AlignCalcWorkerI[] workers;
271     synchronized (canUpdate)
272     {
273       workers = canUpdate.toArray(new AlignCalcWorkerI[0]);
274     }
275     for (AlignCalcWorkerI worker : workers)
276     {
277       if (workerClass.equals(worker.getClass()))
278       {
279         worker.updateAnnotation();
280       }
281     }
282   }
283
284   @Override
285   public List<AlignCalcWorkerI> getRegisteredWorkersOfClass(
286           Class workerClass)
287   {
288     List<AlignCalcWorkerI> workingClass = new ArrayList<AlignCalcWorkerI>();
289     AlignCalcWorkerI[] workers;
290     synchronized (canUpdate)
291     {
292       workers = canUpdate.toArray(new AlignCalcWorkerI[0]);
293     }
294     for (AlignCalcWorkerI worker : workers)
295     {
296       if (workerClass.equals(worker.getClass()))
297       {
298         workingClass.add(worker);
299       }
300     }
301     return (workingClass.size() == 0) ? null : workingClass;
302   }
303
304   @Override
305   public boolean startRegisteredWorkersOfClass(Class workerClass)
306   {
307     List<AlignCalcWorkerI> workers = getRegisteredWorkersOfClass(workerClass);
308     if (workers == null)
309     {
310       return false;
311     }
312     for (AlignCalcWorkerI worker : workers)
313     {
314       if (!isPending(worker))
315       {
316         startWorker(worker);
317       }
318       else
319       {
320         System.err.println("Pending exists for " + workerClass);
321       }
322     }
323     return true;
324   }
325
326   @Override
327   public void workerMayRun(AlignCalcWorkerI worker)
328   {
329     synchronized (blackList)
330     {
331       if (blackList.contains(worker.getClass()))
332       {
333         blackList.remove(worker.getClass());
334       }
335     }
336   }
337
338   @Override
339   public void removeRegisteredWorkersOfClass(Class typeToRemove)
340   {
341     List<AlignCalcWorkerI> workers = getRegisteredWorkersOfClass(typeToRemove);
342     List<AlignCalcWorkerI> removable = new ArrayList<AlignCalcWorkerI>();
343     Set<AlignCalcWorkerI> toremovannot = new HashSet<AlignCalcWorkerI>();
344     synchronized (restartable)
345     {
346       for (AlignCalcWorkerI worker : restartable)
347       {
348         if (typeToRemove.equals(worker.getClass()))
349         {
350           removable.add(worker);
351           toremovannot.add(worker);
352         }
353       }
354       restartable.removeAll(removable);
355     }
356     synchronized (canUpdate)
357     {
358       for (AlignCalcWorkerI worker : canUpdate)
359       {
360         if (typeToRemove.equals(worker.getClass()))
361         {
362           removable.add(worker);
363           toremovannot.add(worker);
364         }
365       }
366       canUpdate.removeAll(removable);
367     }
368     // TODO: finish testing this extension
369
370     /*
371      * synchronized (inProgress) { // need to kill or mark as dead any running
372      * threads... (inProgress.get(typeToRemove)); }
373      * 
374      * if (workers == null) { return; } for (AlignCalcWorkerI worker : workers)
375      * {
376      * 
377      * if (isPending(worker)) { worker.abortAndDestroy(); startWorker(worker); }
378      * else { System.err.println("Pending exists for " + workerClass); } }
379      */
380   }
381 }