Colour be RNA helices bug fixed
[jalview.git] / src / jalview / ws / jws2 / RNAalifoldClient.java
1 package jalview.ws.jws2;
2
3 import jalview.api.AlignCalcWorkerI;
4 import jalview.datamodel.AlignmentAnnotation;
5 import jalview.datamodel.Annotation;
6 import jalview.gui.AlignFrame;
7 import jalview.ws.jws2.dm.AAConSettings;
8 import jalview.ws.jws2.jabaws2.Jws2Instance;
9 import jalview.ws.params.WsParamSetI;
10
11 import java.text.MessageFormat;
12 import java.util.ArrayList;
13 import java.util.LinkedHashMap;
14 import java.util.List;
15 import java.util.TreeSet;
16 import java.util.regex.Pattern;
17
18 import compbio.data.sequence.RNAStructReader.AlifoldResult;
19 import compbio.data.sequence.RNAStructScoreManager;
20 import compbio.data.sequence.Range;
21 import compbio.data.sequence.Score;
22 import compbio.metadata.Argument;
23
24 public class RNAalifoldClient extends JabawsAlignCalcWorker implements
25                 AlignCalcWorkerI 
26 {
27         
28         String methodName;
29         
30         AlignFrame af;
31         
32         // keeps track of whether the RNAalifold result includes base contact probabilities
33         boolean bpScores;
34         
35         public RNAalifoldClient(Jws2Instance sh, AlignFrame alignFrame,
36                         WsParamSetI preset, List<Argument> paramset)
37         {
38                 super(sh, alignFrame, preset, paramset);
39                 
40                 if (arguments == null) arguments = new ArrayList<Argument>();
41
42                 
43                 af = alignFrame;
44                 methodName = sh.serviceType;
45                 
46                 nucleotidesAllowed = true;
47                 proteinAllowed = false;
48                 initViewportParams();
49   }
50
51   protected void initViewportParams()
52   {
53     ((jalview.gui.AlignViewport) alignViewport).setCalcIdSettingsFor(
54             getCalcId(),
55             new AAConSettings(true, service, this.preset,
56                     (arguments != null) ? JabaParamStore
57                             .getJwsArgsfromJaba(arguments) : null), true);
58   }
59         
60         @Override
61         public String getServiceActionText()
62         {
63                 return "Submitting RNA alignment for Secondary Structure prediction using "
64                                 + "RNAalifold Service";
65         }
66
67
68         @Override
69         public void updateResultAnnotation(boolean immediate)
70         {
71
72                 if (immediate || !calcMan.isWorking(this) && scoremanager != null) 
73                 {
74                         
75                         List<AlignmentAnnotation> ourAnnot = new ArrayList<AlignmentAnnotation>();
76
77                         // Unpack the ScoreManager
78                         List<String> structs = ((RNAStructScoreManager) scoremanager).getStructs();
79                         List<TreeSet<Score>> data = ((RNAStructScoreManager) scoremanager).getData();
80                         
81                         // test to see if this data object contains base pair contacts 
82                         Score fscore = data.get(0).first();
83                   this.bpScores = (fscore.getMethod().equals(
84                                         AlifoldResult.contactProbabilities.toString()));
85
86                   // add annotation for the consensus sequence alignment
87                   createAnnotationRowforScoreHolder(ourAnnot, getCalcId(), structs.get(0),
88                                 null, null);
89
90                   // Add annotations for the mfe Structure 
91                   createAnnotationRowforScoreHolder(ourAnnot, getCalcId(), structs.get(1),
92                                 data.get(1), null);
93                   
94                   // decide whether to add base pair contact probability histogram
95                   int count = 2;
96                   if (bpScores) {
97                         createAnnotationRowforScoreHolder(ourAnnot, getCalcId(), structs.get(2),
98                                         data.get(0), data.get(2));
99                         count++;
100                   }
101                   
102                   
103                   // Now loop for the rest of the Annotations (if there it isn't stochastic output
104                   //  only the centroid and MEA structures remain anyway)
105                   for (int i = count; i < structs.size(); i++) {
106                         // The ensemble values should be displayed in the description of the 
107                         // first (or all?) Stochastic Backtrack Structures.
108                         if (!data.get(i).first().getMethod().equals(
109                                         AlifoldResult.ensembleValues.toString())) {
110                                 
111                                 createAnnotationRowforScoreHolder(ourAnnot, getCalcId(), structs.get(i),
112                                                 data.get(i), null);
113                         }
114                         }
115                         
116                         if (ourAnnot.size() > 0) {
117                         
118                                 updateOurAnnots(ourAnnot);
119                                 ap.adjustAnnotationHeight();
120                         }
121                 }
122         }
123         
124         protected void createAnnotationRowforScoreHolder(
125                         List<AlignmentAnnotation> ourAnnot, String calcId, 
126                         String struct, TreeSet<Score> data, TreeSet<Score> descriptionData)
127         {
128                 /* If contactProbability information is returned from RNAalifold it is stored
129                  * in the first TreeSet<Score> object corresponding to the String Id which
130                  * holds the consensus alignment. The method enumeration is then updated to
131                  * AlifoldResult.contactProbabilties. This line recreates the same 
132                  * data object as was overwritten with the contact probabilites data.
133                  */
134                 if (data == null) data = compbio.data.sequence.RNAStructReader
135                                 .newEmptyScore(AlifoldResult.consensusAlignment);
136                 
137                 if (descriptionData == null) descriptionData = data;
138                 
139                 String[] typenameAndDescription = constructTypenameAndDescription(
140                                 descriptionData.first());
141                 String typename = typenameAndDescription[0];
142                 String description = typenameAndDescription[1];
143                 
144                 
145                 AlignmentAnnotation annotation = alignViewport.getAlignment()
146                                         .findOrCreateAnnotation(typename, calcId, false, null, null);
147                                 
148                 constructAnnotationFromScoreHolder(annotation, struct, data);
149                 
150                 /*  update annotation description with the free Energy, frequency in ensemble 
151                  *  or other data where appropriate.
152                  *  
153                  *  Doesnt deal with AlifoldResult.ensembleValues, the free energy of ensemble
154                  *  and frequency of mfe structure in ensemble. How to deal with these? 
155                  */
156                 annotation.description = description;
157                 
158                 annotation.belowAlignment = false;
159 //              annotation.showAllColLabels = true;
160                 
161                 alignViewport.getAlignment().validateAnnotation(annotation);
162                 af.setMenusForViewport();
163                 
164                 ourAnnot.add(annotation);
165         }
166         
167
168
169         private AlignmentAnnotation constructAnnotationFromScoreHolder(
170                         AlignmentAnnotation annotation, String struct, TreeSet<Score> data) 
171         {
172                 Annotation[] anns = new Annotation[struct.length()];
173
174                 if (data != null && data.size() > 1 && data.first().getMethod().equals(
175                                 AlifoldResult.contactProbabilities.toString())) 
176                 {
177
178                         // The base pair probabilities are stored in a set in scoreholder. we want a map
179                         LinkedHashMap<Range, Float> basePairs = new LinkedHashMap<Range, Float>();
180                         for (Score score : data) {
181                                 // The Score objects contain a set of size one containing the range and
182                                 //  an ArrayList<float> of size one containing the probabilty
183                                 basePairs.put(score.getRanges().first(), new Float(score.getScores().get(0)));
184                         }
185                         for (int i = 0; i < struct.length(); i++) {
186
187                                 // Return all the contacts associated with position i
188                                 LinkedHashMap<Range, Float> contacts = isContact(basePairs, i+1);
189
190                                 String description = "";
191                                 float prob = 0f;
192
193                                 if (contacts.size() == 0) {
194                                         description = "No Data";
195                                 }
196                                 else {
197                                         for (Range contact : contacts.keySet()) {
198                                                 float t = contacts.get(contact);
199                                                 if (t > prob) prob = t;
200                                                 description += Integer.toString(contact.from) + "->" 
201                                                                 + Integer.toString(contact.to) +  ": " + Float.toString(t) + "%  |  "; 
202                                         }
203                                 }
204
205                                 anns[i] = new Annotation(struct.substring(i, i+1), description,
206                                                 isSS(struct.charAt(i)), prob);
207                         }
208                 }
209                 else if (data == null || data.size() == 1) {
210                         for (int i = 0; i < struct.length(); i++) {
211                                 
212                                 anns[i] = new Annotation(struct.substring(i, i+1), "",
213                                                 isSS(struct.charAt(i)), Float.NaN);
214                         }
215                         
216                         annotation.graph = 0; // No graph
217                 }
218                 
219                 annotation.annotations = anns;
220                 
221                 return annotation;
222         }
223         
224         private String[] constructTypenameAndDescription(Score score) {
225                 String description = "";
226                 String typename = "";
227                 String datatype = score.getMethod();
228                 
229                 
230                 // Look up java switch syntax and use one here
231                 if (datatype.equals(AlifoldResult.mfeStructure.toString())) {
232                         
233                         description = MessageFormat.format("Minimum Free Energy Structure. Energy: {0} = {1} + {2}", 
234                                         score.getScores().get(0), score.getScores().get(1), score.getScores().get(2));
235                         typename = "MFE Structure";
236                 }
237                 else if (datatype.equals(AlifoldResult.contactProbabilityStructure.toString())) {
238                         description = MessageFormat.format("Base Pair Contact Probabilities. "
239                                         + "Energy of Ensemble: {0}  Frequency of Ensemble: {1}",
240                                         score.getScores().get(0), score.getScores().get(1));
241                         typename = "Contact Probabilities";
242                 }
243                 else if (datatype.equals(AlifoldResult.centroidStructure.toString())) {
244                         description = MessageFormat.format("Centroid Structure. Energy: {0} = {1} + {2}", 
245                                         score.getScores().get(0), score.getScores().get(1), score.getScores().get(2));
246                         typename = "Centroid Structure";
247                 }
248                 else if (datatype.equals(AlifoldResult.stochBTStructure.toString())) {
249                         if (score.getScores().size() > 0) {
250                                 description = MessageFormat.format("Probability: {0}  Energy: {1}", 
251                                                 score.getScores().get(0), score.getScores().get(1));
252                         }
253                         else description = "Stochastic Backtrack Structure";
254                 }
255                 else if (datatype.equals(AlifoldResult.MEAStucture.toString())) {
256                         description = MessageFormat.format("Maximum Expected Accuracy Values: '{' {0} MEA={1} '}",
257                                                 score.getScores().get(0), score.getScores().get(1));
258                         typename = "MEA Structure";
259                 }
260                 else if (datatype.equals(AlifoldResult.consensusAlignment.toString())) {
261                         typename = "RNAalifold Consensus";
262                         description = "Consensus Alignment Produced by RNAalifold";
263                 }
264                 else {
265                         typename = datatype;
266                         description = typename;
267                 }
268                 
269                 return new String[] {typename, description};
270         }
271         
272         // Check whether, at position i there is a base contact and return all the
273         //  contacts at this position. Should be in order of descending probability.
274         private LinkedHashMap<Range, Float> isContact(LinkedHashMap<Range,
275                         Float> basePairs, int i) 
276                 {
277                 LinkedHashMap<Range, Float> contacts = new LinkedHashMap<Range, Float>();
278                 
279                 for (Range contact : basePairs.keySet()) {
280                         // finds the contacts associtated with position i ordered by the natural
281                         //  ordering of the Scores TreeSet in ScoreManager which is, descending probability
282                         if (contact.from == i || contact.to == i) 
283                                 contacts.put(contact, basePairs.get(contact));
284                 }
285                 
286                 return contacts;
287         }
288         
289         private char isSS (char chr) {
290                 String regex = "\\(|\\)|\\{|\\}|\\[|\\]"; 
291                 char ss = (Pattern.matches(regex, Character.toString(chr))) ? 'S': ' ';
292                 return ss;
293         }
294
295         public String getCalcId()
296   {
297     return SequenceAnnotationWSClient.AAConCalcId;
298   }
299         
300 }