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