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