JAL-2089 patch broken merge to master for Release 2.10.0b1
[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.SequenceI;
30 import jalview.schemes.ColourSchemeI;
31
32 import java.util.Hashtable;
33
34 public class ConsensusThread extends AlignCalcWorker
35 {
36   public ConsensusThread(AlignViewportI alignViewport,
37           AlignmentViewPanel alignPanel)
38   {
39     super(alignViewport, alignPanel);
40   }
41
42   @Override
43   public void run()
44   {
45     if (calcMan.isPending(this))
46     {
47       return;
48     }
49     calcMan.notifyStart(this);
50     long started = System.currentTimeMillis();
51     try
52     {
53       AlignmentAnnotation consensus = getConsensusAnnotation();
54       if (consensus == 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   }
122
123   /**
124    * @param alignment
125    */
126   protected void computeConsensus(AlignmentI alignment)
127   {
128     Hashtable[] hconsensus = new Hashtable[alignment.getWidth()];
129
130     SequenceI[] aseqs = getSequences();
131     AAFrequency.calculate(aseqs, 0, alignment.getWidth(), hconsensus, true);
132
133     alignViewport.setSequenceConsensusHash(hconsensus);
134     setColourSchemeConsensus(hconsensus);
135   }
136
137   /**
138    * @return
139    */
140   protected SequenceI[] getSequences()
141   {
142     return alignViewport.getAlignment().getSequencesArray();
143   }
144
145   /**
146    * @param hconsensus
147    */
148   protected void setColourSchemeConsensus(Hashtable[] hconsensus)
149   {
150     ColourSchemeI globalColourScheme = alignViewport
151             .getGlobalColourScheme();
152     if (globalColourScheme != null)
153     {
154       globalColourScheme.setConsensus(hconsensus);
155     }
156   }
157
158   /**
159    * Get the Consensus annotation for the alignment
160    * 
161    * @return
162    */
163   protected AlignmentAnnotation getConsensusAnnotation()
164   {
165     return alignViewport.getAlignmentConsensusAnnotation();
166   }
167
168   /**
169    * update the consensus annotation from the sequence profile data using
170    * current visualization settings.
171    */
172   @Override
173   public void updateAnnotation()
174   {
175     updateResultAnnotation(false);
176   }
177
178   public void updateResultAnnotation(boolean immediate)
179   {
180     AlignmentAnnotation consensus = getConsensusAnnotation();
181     Hashtable[] hconsensus = getViewportConsensus();
182     if (immediate || !calcMan.isWorking(this) && consensus != null
183             && hconsensus != null)
184     {
185       deriveConsensus(consensus, hconsensus);
186     }
187   }
188
189   /**
190    * Convert the computed consensus data into the desired annotation for
191    * display.
192    * 
193    * @param consensusAnnotation
194    *          the annotation to be populated
195    * @param consensusData
196    *          the computed consensus data
197    */
198   protected void deriveConsensus(AlignmentAnnotation consensusAnnotation,
199           Hashtable[] consensusData)
200   {
201     long nseq = getSequences().length;
202     AAFrequency.completeConsensus(consensusAnnotation, consensusData, 0,
203             consensusData.length, alignViewport.isIgnoreGapsConsensus(),
204             alignViewport.isShowSequenceLogo(), nseq);
205   }
206
207   /**
208    * Get the consensus data stored on the viewport.
209    * 
210    * @return
211    */
212   protected Hashtable[] getViewportConsensus()
213   {
214     return alignViewport.getSequenceConsensusHash();
215   }
216 }