Merge branch 'develop' into feature/JAL-4386_calculate_tree_using_secondary_structure...
[jalview.git] / src / jalview / workers / SecondaryStructureConsensusThread.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 SecondaryStructureConsensusThread extends AlignCalcWorker
34 {
35   public SecondaryStructureConsensusThread(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 ssConsensus = getSSConsensusAnnotation();
53       AlignmentAnnotation gap = getGapAnnotation();
54       if ((ssConsensus == null && gap == null) || calcMan.isPending(this))
55       {
56         calcMan.workerComplete(this);
57         return;
58       }
59       while (!calcMan.notifyWorking(this))
60       {
61         try
62         {
63           if (ap != null)
64           {
65             ap.paintAlignment(false, 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       eraseSSConsensus(aWidth);
89       computeSSConsensus(alignment);
90       updateResultAnnotation(true);
91
92       if (ap != null)
93       {
94         ap.paintAlignment(true, 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 eraseSSConsensus(int aWidth)
117   {
118     AlignmentAnnotation ssConsensus = getSSConsensusAnnotation();
119     if (ssConsensus != null)
120     {
121       ssConsensus.annotations = new Annotation[aWidth];
122     }
123     AlignmentAnnotation gap = getGapAnnotation();
124     if (gap != null)
125     {
126       gap.annotations = new Annotation[aWidth];
127     }
128   }
129
130   /**
131    * @param alignment
132    */
133   protected void computeSSConsensus(AlignmentI alignment)
134   {
135
136     SequenceI[] aseqs = getSequences();
137     int width = alignment.getWidth();
138     ProfilesI hSSConsensus = AAFrequency.calculateSS(aseqs, width, 0, width,
139             true);
140
141     alignViewport.setSequenceSSConsensusHash(hSSConsensus);
142     setColourSchemeConsensus(hSSConsensus);
143   }
144
145   /**
146    * @return
147    */
148   protected SequenceI[] getSequences()
149   {
150     return alignViewport.getAlignment().getSequencesArray();
151   }
152
153   /**
154    * @param hconsensus
155    */
156   protected void setColourSchemeConsensus(ProfilesI hSSconsensus)
157   {
158     ResidueShaderI cs = alignViewport.getResidueShading();
159     if (cs != null)
160     {
161       cs.setSsConsensus(hSSconsensus);
162     }
163   }
164
165   /**
166    * Get the Consensus annotation for the alignment
167    * 
168    * @return
169    */
170   protected AlignmentAnnotation getSSConsensusAnnotation()
171   {
172     return alignViewport.getAlignmentSecondaryStructureConsensusAnnotation();
173   }
174
175   /**
176    * Get the Gap annotation for the alignment
177    * 
178    * @return
179    */
180   protected AlignmentAnnotation getGapAnnotation()
181   {
182     return alignViewport.getAlignmentGapAnnotation();
183   }
184
185   /**
186    * update the consensus annotation from the sequence profile data using
187    * current visualization settings.
188    */
189   @Override
190   public void updateAnnotation()
191   {
192     updateResultAnnotation(false);
193   }
194
195   public void updateResultAnnotation(boolean immediate)
196   {
197     AlignmentAnnotation ssConsensus = getSSConsensusAnnotation();
198     ProfilesI hSSConsensus = (ProfilesI) getViewportSSConsensus();
199     if (immediate || !calcMan.isWorking(this) && ssConsensus != null
200             && hSSConsensus != null)
201     {
202       deriveSSConsensus(ssConsensus, hSSConsensus);
203       AlignmentAnnotation gap = getGapAnnotation();
204       if (gap != null)
205       {
206         deriveGap(gap, hSSConsensus);
207       }
208     }
209   }
210
211   /**
212    * Convert the computed consensus data into the desired annotation for
213    * display.
214    * 
215    * @param consensusAnnotation
216    *          the annotation to be populated
217    * @param hconsensus
218    *          the computed consensus data
219    */
220   protected void deriveSSConsensus(AlignmentAnnotation ssConsensusAnnotation,
221           ProfilesI hSSConsensus)
222   {
223
224     long nseq = getSequences().length;
225     AAFrequency.completeSSConsensus(ssConsensusAnnotation, hSSConsensus,
226             hSSConsensus.getStartColumn(), hSSConsensus.getEndColumn() + 1,
227             alignViewport.isIgnoreGapsConsensus(),
228             alignViewport.isShowSequenceLogo(), nseq);
229   }
230
231   /**
232    * Convert the computed consensus data into a gap annotation row for display.
233    * 
234    * @param gapAnnotation
235    *          the annotation to be populated
236    * @param hconsensus
237    *          the computed consensus data
238    */
239   protected void deriveGap(AlignmentAnnotation gapAnnotation,
240           ProfilesI hconsensus)
241   {
242     long nseq = getSequences().length;
243     AAFrequency.completeGapAnnot(gapAnnotation, hconsensus,
244             hconsensus.getStartColumn(), hconsensus.getEndColumn() + 1,
245             nseq);
246   }
247
248   /**
249    * Get the consensus data stored on the viewport.
250    * 
251    * @return
252    */
253   protected Object getViewportSSConsensus()
254   {
255     // TODO convert ComplementConsensusThread to use Profile
256     return alignViewport.getSequenceSSConsensusHash();
257   }
258 }