JAL-1807 Bob's JalviewJS prototype first commit
[jalviewjs.git] / src / jalview / workers / ConsensusThread.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.analysis.AAFrequency;
24 import jalview.api.AlignCalcWorkerI;
25 import jalview.api.AlignViewportI;
26 import jalview.api.AlignmentViewPanel;
27 import jalview.datamodel.AlignmentAnnotation;
28 import jalview.datamodel.AlignmentI;
29 import jalview.datamodel.Annotation;
30 import jalview.datamodel.SequenceI;
31 import jalview.schemes.ColourSchemeI;
32
33 import java.util.Hashtable;
34
35 public class ConsensusThread extends AlignCalcWorker implements
36         AlignCalcWorkerI
37 {
38         
39         protected Hashtable<String, Object>[] hconsensus;
40         protected SequenceI[] aseqs;
41
42         @Override
43         public AlignCalcWorkerI getNewWorker() {
44                 return new ConsensusThread(alignViewport, ap);
45         }
46
47         public ConsensusThread(AlignViewportI alignViewport,
48           AlignmentViewPanel alignPanel)
49   {
50     super(alignViewport, alignPanel);
51   }
52
53
54         /**
55          * The basic idea is that the state starts INIT, and then you can advance it as you
56          * wish to one or more LOOP states, and then to DONE.
57          * 
58          * The entire operation is within a while loop so that Java need not exit. 
59          * 
60          * You can test for JavaScript using the field isJS.
61          * 
62          * JSThread simply executes sleepAndReturn() at the end of these loops, returning 
63          * TRUE if it is necessary to exit the thread (because this is JavaScript). In the
64          * case of Java, we are simply executing sleep(n), so we embed all this in
65          * while() loops.
66          * 
67          */
68         @Override
69         protected void run1(int state) {
70                 while (!interrupted()) {
71       if (alignViewport.isClosed())
72       {
73         abortAndDestroy();
74         return;
75       }
76                         try {
77                                 switch (state) {
78                                 case INIT:
79                                         if (calcMan.isPending(this))
80                                                 return;
81                                         calcMan.notifyStart(this);
82                                         AlignmentAnnotation consensus = getConsensusAnnotation();
83                                         if (consensus == null || calcMan.isPending(this)) {
84                                                 calcMan.workerComplete(this);
85                                                 return;
86                                         }
87                                         state = LOOP_STANDBY;
88                                         break;
89                                 case LOOP_STANDBY:
90                                         while (!calcMan.notifyWorking(this)) {
91                                                 if (ap != null) {
92                                                         ap.paintAlignment(false);
93                                                 }
94                                                 try {
95                                                         if (sleepAndReturn(200, state))
96                                                                 return;
97                                                 } catch (InterruptedException e) {
98                                                         state = DONE;
99                                                         break;
100                                                 }
101                                         }
102                                         if (alignViewport.isClosed()) {
103                                                 abortAndDestroy();
104                                                 state = DONE;
105                                                 break;
106                                         }
107                                         alignment = alignViewport.getAlignment();
108                                         aWidth = -1;
109                                         if (alignment == null || (aWidth = alignment.getWidth()) < 0) {
110                                                 state = DONE;
111                                         }
112                                         eraseConsensus(aWidth);
113                                         state = (initializeCalc() ? LOOP_CALCULATE : DONE);
114                                         break;
115                                 case LOOP_CALCULATE:
116                                         iFirst = iLast;
117                                         iLast = Math.min(iLast + nPer, aWidth);
118                                         if (iLast == iFirst) {
119                                                 state = DONE;
120                                         } else {
121                                                 computeConsensus();
122                                                 if (sleepAndReturn(0, state))
123                                                         return;
124                                         }
125                                         break;
126                                 case DONE:
127                                         finalizeCalc();
128                                         updateAlignment();
129                                         notifyDone();
130                                         return;
131                                 }
132                         } catch (OutOfMemoryError error) {
133                                 calcMan.workerCannotRun(this);
134                                 ap.raiseOOMWarning("calculating consensus", error);
135                         } catch (Throwable e) {
136                                 System.out.println("Error in ConsensusThread: " + e);
137                                 e.printStackTrace();
138                                 calcMan.workerComplete(this);
139                         }
140                 }
141         }
142
143         @SuppressWarnings("unchecked")
144   protected boolean initializeCalc() {
145                 iLast = 0;
146                 hconsensus = new Hashtable[aWidth];
147                 aseqs = getSequences();
148                 return true;
149         }
150
151         protected void computeConsensus() {
152                 started = System.currentTimeMillis();
153                 AAFrequency.calculate(aseqs, iFirst, iLast, hconsensus, true);
154                 if (System.currentTimeMillis() - started < MS_MAX)
155                         nPer *= 2;      
156   }
157
158         protected void finalizeCalc() {
159                 // BH: I was not sure about the exact placement of each of these steps
160                 alignViewport.setSequenceConsensusHash(hconsensus);
161         }
162
163         protected void updateAlignment() {
164                 setColourSchemeConsensus(hconsensus);
165                 updateResultAnnotation(true);
166         }
167
168
169   /**
170    * Clear out any existing consensus annotations
171    * 
172    * @param aWidth
173    *          the width (number of columns) of the annotated alignment
174    */
175   protected void eraseConsensus(int aWidth)
176   {
177     AlignmentAnnotation consensus = getConsensusAnnotation();
178     consensus.annotations = new Annotation[aWidth];
179   }
180
181   /**
182    * @return
183    */
184   protected SequenceI[] getSequences()
185   {
186     return alignViewport.getAlignment().getSequencesArray();
187   }
188
189   /**
190    * @param hconsensus
191    */
192   protected void setColourSchemeConsensus(Hashtable[] hconsensus)
193   {
194     ColourSchemeI globalColourScheme = alignViewport
195             .getGlobalColourScheme();
196     if (globalColourScheme != null)
197     {
198       globalColourScheme.setConsensus(hconsensus);
199     }
200   }
201
202   /**
203    * Get the Consensus annotation for the alignment
204    * 
205    * @return
206    */
207   protected AlignmentAnnotation getConsensusAnnotation()
208   {
209     return alignViewport.getAlignmentConsensusAnnotation();
210   }
211
212   /**
213    * update the consensus annotation from the sequence profile data using
214    * current visualization settings.
215    */
216   @Override
217   public void updateAnnotation()
218   {
219     updateResultAnnotation(false);
220   }
221
222   public void updateResultAnnotation(boolean immediate)
223   {
224     AlignmentAnnotation consensus = getConsensusAnnotation();
225     Hashtable[] hconsensus = getViewportConsensus();
226     if (immediate || !calcMan.isWorking(this) && consensus != null
227             && hconsensus != null)
228     {
229       deriveConsensus(consensus, hconsensus);
230     }
231   }
232
233   /**
234    * Convert the computed consensus data into the desired annotation for
235    * display.
236    * 
237    * @param consensusAnnotation
238    *          the annotation to be populated
239    * @param consensusData
240    *          the computed consensus data
241    */
242   protected void deriveConsensus(AlignmentAnnotation consensusAnnotation,
243           Hashtable[] consensusData)
244   {
245     long nseq = getSequences().length;
246     AAFrequency.completeConsensus(consensusAnnotation, consensusData, 0,
247             consensusData.length, alignViewport.isIgnoreGapsConsensus(),
248             alignViewport.isShowSequenceLogo(), nseq);
249   }
250
251   /**
252    * Get the consensus data stored on the viewport.
253    * 
254    * @return
255    */
256   protected Hashtable[] getViewportConsensus()
257   {
258     return alignViewport.getSequenceConsensusHash();
259   }
260
261
262 }