Merge remote-tracking branch 'origin/bug/JAL-2282' into develop
[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.ProfileI;
30 import jalview.datamodel.SequenceI;
31 import jalview.schemes.ColourSchemeI;
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       if (consensus == null || calcMan.isPending(this))
54       {
55         calcMan.workerComplete(this);
56         return;
57       }
58       while (!calcMan.notifyWorking(this))
59       {
60         // System.err.println("Thread (Consensus"+Thread.currentThread().getName()+") Waiting around.");
61         try
62         {
63           if (ap != null)
64           {
65             ap.paintAlignment(false);
66           }
67           Thread.sleep(200);
68         } catch (Exception ex)
69         {
70           ex.printStackTrace();
71         }
72       }
73       if (alignViewport.isClosed())
74       {
75         abortAndDestroy();
76         return;
77       }
78       AlignmentI alignment = alignViewport.getAlignment();
79
80       int aWidth = -1;
81
82       if (alignment == null || (aWidth = alignment.getWidth()) < 0)
83       {
84         calcMan.workerComplete(this);
85         return;
86       }
87
88       eraseConsensus(aWidth);
89       computeConsensus(alignment);
90       updateResultAnnotation(true);
91
92       if (ap != null)
93       {
94         ap.paintAlignment(true);
95       }
96     } catch (OutOfMemoryError error)
97     {
98       calcMan.disableWorker(this);
99       ap.raiseOOMWarning("calculating consensus", error);
100     } finally
101     {
102       /*
103        * e.g. ArrayIndexOutOfBoundsException can happen due to a race condition
104        * - alignment was edited at same time as calculation was running
105        */
106       calcMan.workerComplete(this);
107     }
108   }
109
110   /**
111    * Clear out any existing consensus annotations
112    * 
113    * @param aWidth
114    *          the width (number of columns) of the annotated alignment
115    */
116   protected void eraseConsensus(int aWidth)
117   {
118     AlignmentAnnotation consensus = getConsensusAnnotation();
119     consensus.annotations = new Annotation[aWidth];
120   }
121
122   /**
123    * @param alignment
124    */
125   protected void computeConsensus(AlignmentI alignment)
126   {
127     ProfileI[] hconsensus = new ProfileI[alignment.getWidth()];
128
129     SequenceI[] aseqs = getSequences();
130     AAFrequency.calculate(aseqs, 0, alignment.getWidth(), hconsensus, true);
131
132     alignViewport.setSequenceConsensusHash(hconsensus);
133     setColourSchemeConsensus(hconsensus);
134   }
135
136   /**
137    * @return
138    */
139   protected SequenceI[] getSequences()
140   {
141     return alignViewport.getAlignment().getSequencesArray();
142   }
143
144   /**
145    * @param hconsensus
146    */
147   protected void setColourSchemeConsensus(ProfileI[] hconsensus)
148   {
149     ColourSchemeI globalColourScheme = alignViewport
150             .getGlobalColourScheme();
151     if (globalColourScheme != null)
152     {
153       globalColourScheme.setConsensus(hconsensus);
154     }
155   }
156
157   /**
158    * Get the Consensus annotation for the alignment
159    * 
160    * @return
161    */
162   protected AlignmentAnnotation getConsensusAnnotation()
163   {
164     return alignViewport.getAlignmentConsensusAnnotation();
165   }
166
167   /**
168    * update the consensus annotation from the sequence profile data using
169    * current visualization settings.
170    */
171   @Override
172   public void updateAnnotation()
173   {
174     updateResultAnnotation(false);
175   }
176
177   public void updateResultAnnotation(boolean immediate)
178   {
179     AlignmentAnnotation consensus = getConsensusAnnotation();
180     ProfileI[] hconsensus = (ProfileI[]) getViewportConsensus();
181     if (immediate || !calcMan.isWorking(this) && consensus != null
182             && hconsensus != null)
183     {
184       deriveConsensus(consensus, hconsensus);
185     }
186   }
187
188   /**
189    * Convert the computed consensus data into the desired annotation for
190    * display.
191    * 
192    * @param consensusAnnotation
193    *          the annotation to be populated
194    * @param hconsensus
195    *          the computed consensus data
196    */
197   protected void deriveConsensus(AlignmentAnnotation consensusAnnotation,
198           ProfileI[] hconsensus)
199   {
200     long nseq = getSequences().length;
201     AAFrequency.completeConsensus(consensusAnnotation, hconsensus, 0,
202             hconsensus.length, alignViewport.isIgnoreGapsConsensus(),
203             alignViewport.isShowSequenceLogo(), nseq);
204   }
205
206   /**
207    * Get the consensus data stored on the viewport.
208    * 
209    * @return
210    */
211   protected Object[] getViewportConsensus()
212   {
213     // TODO convert ComplementConsensusThread to use Profile
214     return alignViewport.getSequenceConsensusHash();
215   }
216 }