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