JAL-2587 prevented double overview recalc on opening overview
[jalview.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.AlignViewportI;
25 import jalview.api.AlignmentViewPanel;
26 import jalview.datamodel.AlignmentAnnotation;
27 import jalview.datamodel.AlignmentI;
28 import jalview.datamodel.Annotation;
29 import jalview.datamodel.ProfilesI;
30 import jalview.datamodel.SequenceI;
31 import jalview.renderer.ResidueShaderI;
32
33 public class ConsensusThread extends AlignCalcWorker
34 {
35   public ConsensusThread(AlignViewportI alignViewport,
36           AlignmentViewPanel alignPanel)
37   {
38     super(alignViewport, alignPanel);
39   }
40
41   @Override
42   public void run()
43   {
44     if (calcMan.isPending(this))
45     {
46       return;
47     }
48     calcMan.notifyStart(this);
49     long started = System.currentTimeMillis();
50     try
51     {
52       AlignmentAnnotation consensus = getConsensusAnnotation();
53       AlignmentAnnotation gap = getGapAnnotation();
54       if ((consensus == null && gap == null) || calcMan.isPending(this))
55       {
56         calcMan.workerComplete(this);
57         return;
58       }
59       while (!calcMan.notifyWorking(this))
60       {
61         // System.err.println("Thread (Consensus"+Thread.currentThread().getName()+") Waiting around.");
62         try
63         {
64           if (ap != null)
65           {
66             ap.paintAlignment(false);
67           }
68           Thread.sleep(200);
69         } catch (Exception ex)
70         {
71           ex.printStackTrace();
72         }
73       }
74       if (alignViewport.isClosed())
75       {
76         abortAndDestroy();
77         return;
78       }
79       AlignmentI alignment = alignViewport.getAlignment();
80
81       int aWidth = -1;
82
83       if (alignment == null || (aWidth = alignment.getWidth()) < 0)
84       {
85         calcMan.workerComplete(this);
86         return;
87       }
88
89       eraseConsensus(aWidth);
90       computeConsensus(alignment);
91       updateResultAnnotation(true);
92
93       if (ap != null)
94       {
95         ap.paintAlignment(true);
96       }
97     } catch (OutOfMemoryError error)
98     {
99       calcMan.disableWorker(this);
100       ap.raiseOOMWarning("calculating consensus", error);
101     } finally
102     {
103       /*
104        * e.g. ArrayIndexOutOfBoundsException can happen due to a race condition
105        * - alignment was edited at same time as calculation was running
106        */
107       calcMan.workerComplete(this);
108     }
109   }
110
111   /**
112    * Clear out any existing consensus annotations
113    * 
114    * @param aWidth
115    *          the width (number of columns) of the annotated alignment
116    */
117   protected void eraseConsensus(int aWidth)
118   {
119     AlignmentAnnotation consensus = getConsensusAnnotation();
120     consensus.annotations = new Annotation[aWidth];
121     AlignmentAnnotation gap = getGapAnnotation();
122     if (gap != null)
123     {
124       gap.annotations = new Annotation[aWidth];
125     }
126   }
127
128   /**
129    * @param alignment
130    */
131   protected void computeConsensus(AlignmentI alignment)
132   {
133
134     SequenceI[] aseqs = getSequences();
135     int width = alignment.getWidth();
136     ProfilesI hconsensus = AAFrequency.calculate(aseqs, width, 0, width,
137             true);
138
139     alignViewport.setSequenceConsensusHash(hconsensus);
140     setColourSchemeConsensus(hconsensus);
141   }
142
143   /**
144    * @return
145    */
146   protected SequenceI[] getSequences()
147   {
148     return alignViewport.getAlignment().getSequencesArray();
149   }
150
151   /**
152    * @param hconsensus
153    */
154   protected void setColourSchemeConsensus(ProfilesI hconsensus)
155   {
156     ResidueShaderI cs = alignViewport.getResidueShading();
157     if (cs != null)
158     {
159       cs.setConsensus(hconsensus);
160     }
161   }
162
163   /**
164    * Get the Consensus annotation for the alignment
165    * 
166    * @return
167    */
168   protected AlignmentAnnotation getConsensusAnnotation()
169   {
170     return alignViewport.getAlignmentConsensusAnnotation();
171   }
172
173   /**
174    * Get the Gap annotation for the alignment
175    * 
176    * @return
177    */
178   protected AlignmentAnnotation getGapAnnotation()
179   {
180     return alignViewport.getAlignmentGapAnnotation();
181   }
182
183   /**
184    * update the consensus annotation from the sequence profile data using
185    * current visualization settings.
186    */
187   @Override
188   public void updateAnnotation()
189   {
190     updateResultAnnotation(false);
191   }
192
193   public void updateResultAnnotation(boolean immediate)
194   {
195     AlignmentAnnotation consensus = getConsensusAnnotation();
196     ProfilesI hconsensus = (ProfilesI) getViewportConsensus();
197     if (immediate || !calcMan.isWorking(this) && consensus != null
198             && hconsensus != null)
199     {
200       deriveConsensus(consensus, hconsensus);
201       AlignmentAnnotation gap = getGapAnnotation();
202       if (gap != null)
203       {
204         deriveGap(gap, hconsensus);
205       }
206     }
207   }
208
209   /**
210    * Convert the computed consensus data into the desired annotation for
211    * display.
212    * 
213    * @param consensusAnnotation
214    *          the annotation to be populated
215    * @param hconsensus
216    *          the computed consensus data
217    */
218   protected void deriveConsensus(AlignmentAnnotation consensusAnnotation,
219           ProfilesI hconsensus)
220   {
221
222     long nseq = getSequences().length;
223     AAFrequency.completeConsensus(consensusAnnotation, hconsensus,
224             hconsensus.getStartColumn(), hconsensus.getEndColumn() + 1,
225             alignViewport.isIgnoreGapsConsensus(),
226             alignViewport.isShowSequenceLogo(), nseq);
227   }
228
229   /**
230    * Convert the computed consensus data into a gap annotation row for display.
231    * 
232    * @param gapAnnotation
233    *          the annotation to be populated
234    * @param hconsensus
235    *          the computed consensus data
236    */
237   protected void deriveGap(AlignmentAnnotation gapAnnotation,
238           ProfilesI hconsensus)
239   {
240     long nseq = getSequences().length;
241     AAFrequency.completeGapAnnot(gapAnnotation, hconsensus,
242             hconsensus.getStartColumn(), hconsensus.getEndColumn() + 1,
243             nseq);
244   }
245
246   /**
247    * Get the consensus data stored on the viewport.
248    * 
249    * @return
250    */
251   protected Object getViewportConsensus()
252   {
253     // TODO convert ComplementConsensusThread to use Profile
254     return alignViewport.getSequenceConsensusHash();
255   }
256 }