Merge branch 'develop' into trialmerge/JAL-1950_hmmer3client
[jalview.git] / src / jalview / ws / ebi / HmmerJSONProcessor.java
1 package jalview.ws.ebi;
2
3 import jalview.datamodel.AlignmentAnnotation;
4 import jalview.datamodel.AlignmentI;
5 import jalview.datamodel.Annotation;
6 import jalview.datamodel.SequenceFeature;
7 import jalview.datamodel.SequenceGroup;
8 import jalview.datamodel.SequenceI;
9 import jalview.io.FileParse;
10 import jalview.viewmodel.AlignmentViewport;
11
12 import java.io.IOException;
13 import java.util.ArrayList;
14 import java.util.HashMap;
15 import java.util.List;
16 import java.util.Map;
17
18 import org.json.simple.JSONArray;
19 import org.json.simple.JSONObject;
20 import org.json.simple.parser.JSONParser;
21
22 public class HmmerJSONProcessor
23 {
24   /**
25    * result to be annotated. may not be null
26    */
27   AlignmentI resultAl;
28
29   /**
30    * viewport on the alignment. may be null at construction time
31    */
32   AlignmentViewport viewAl = null;
33
34   public HmmerJSONProcessor(AlignmentI searchResult)
35   {
36     resultAl = searchResult;
37   }
38
39   public void parseFrom(FileParse jsonsource) throws IOException,
40           OutOfMemoryError
41   {
42     JSONParser hmmerResultParser = new JSONParser();
43     Object jsonResults = null;
44     try
45     {
46       jsonResults = hmmerResultParser.parse(jsonsource.getReader());
47     } catch (Exception p)
48     {
49       throw new IOException("While parsing from " + jsonsource.getInFile(),
50               p);
51     }
52     if (jsonResults == null)
53     {
54       throw new IOException("No data at" + jsonsource.getInFile());
55     }
56     if (!(jsonResults instanceof JSONObject))
57     {
58       throw new IOException("Unexpected JSON model at "
59               + jsonsource.getInFile());
60     }
61     try
62     {
63       JSONObject hmmsearchr = (JSONObject) ((JSONObject) jsonResults)
64               .get("results");
65       // now process the hits
66       addStatistics((JSONObject) hmmsearchr.get("stats"));
67       JSONArray jsonArray = (JSONArray) hmmsearchr.get("hits");
68       long p = 1;
69       for (Object hit : jsonArray)
70       {
71         JSONObject hmmhit = (JSONObject) hit;
72         addHit(hmmhit, p++);
73       }
74     } catch (ClassCastException q)
75     {
76       throw new IOException("Unexpected JSON model content at "
77               + jsonsource.getInFile(), q);
78     }
79   }
80
81   /**
82    * 
83    * @param object
84    *          - actually a JSONObject key value set of search statistics.
85    */
86   public void addStatistics(JSONObject stats)
87   {
88     for (Object stat : stats.keySet())
89     {
90       String key = (String) stat;
91       Object val = stats.get(key);
92       resultAl.setProperty(key, "" + val);
93     }
94   }
95
96   // encodings for JSON keys
97   /**
98    * score becomes sequence associated AlignmentAnnotation
99    */
100   private String[] score = { "aliId", "ali_IdCount", "bitscore", "ievalue",
101       "aliSim", "aliSimCount", "aliL", "aliSim", "ievalue", "cevalue" };
102
103   /**
104    * attrib becomes numeric or binary attribute for sequence with respect to
105    * this hmmsearch run
106    */
107   private String[] attrib = { "bias", "oasc", "is_included", "is_reported" };
108
109   /**
110    * name of the hmmsearch query
111    */
112   private String[] label = { "alihmmname" // (query label?)},
113   };
114
115   /**
116    * integer attributes for each
117    */
118   private String[] ipos = { "alihmmfrom", "alihmmto" }, pos_l = {
119       "alimline", "alimodel", "alirfline" };
120
121   /**
122    * positional quantitative annotation encoded as strings.
123    */
124   private String[] pos_nscore = { "alippline" };
125
126   //
127   // mapping of keys to types of property on sequence
128   //
129   public void addHit(JSONObject hmmrhit, long p)
130   {
131     String sname = (String) hmmrhit.get("name");
132     SequenceI[] hits = resultAl.findSequenceMatch(sname);
133     if (hits == null)
134     {
135       System.err.println("No seq for " + sname);
136     }
137     double pvalue = (Double) hmmrhit.get("pvalue");
138
139     double evalue = Double.valueOf("" + hmmrhit.get("evalue"));
140     for (Object domainhit : ((JSONArray) hmmrhit.get("domains")))
141     {
142       JSONObject dhit = (JSONObject) domainhit;
143       // dhit.get(key)
144
145       // alihmmfrom,alihmmto alimodel
146       long alihmmfrom = (long) dhit.get("alihmmfrom"), alihmmto = (long) dhit
147               .get("alihmmto"), alisqfrom = (long) dhit.get("alisqfrom"), alisqto = (long) dhit
148               .get("alisqto");
149
150       // alisqfrom,alisqto,aliaseq
151
152       // alippline
153       String aliaseq = (String) dhit.get("aliaseq"), alimodel = (String) dhit
154               .get("alimodel"), ppline = (String) dhit.get("alippline");
155       //
156       int found = 0;
157       SequenceI firsthit = null;
158       for (SequenceI hitseq : hits)
159       {
160         // match alisqfrom,alisqto,seq
161         if (hitseq.getStart() == alisqfrom && hitseq.getEnd() == alisqto)
162         {
163           if (found == 0)
164           {
165             firsthit = hitseq;
166           }
167           found++; // annotated a sequence
168           AlignmentAnnotation alipp = parsePosteriorProb(ppline);
169           AlignmentAnnotation pval = new AlignmentAnnotation("p-value",
170                   "hmmer3 pvalue", pvalue);
171           AlignmentAnnotation eval = new AlignmentAnnotation("e-value",
172                   "hmmer3 evalue", evalue);
173           pval.setCalcId("HMMER3");
174           eval.setCalcId("HMMER3");
175           alipp.setCalcId("HMMER3");
176           hitseq.addAlignmentAnnotation(pval);
177           hitseq.addAlignmentAnnotation(eval);
178           alipp.createSequenceMapping(hitseq, hitseq.getStart(), false);
179           hitseq.addAlignmentAnnotation(alipp);
180           String arch;
181           hitseq.addSequenceFeature(new SequenceFeature(
182                   "Pfam Domain Architecture", (hmmrhit.get("archindex"))
183                           + " " + (arch = (String) hmmrhit.get("arch")), 0,
184                   0, Integer.valueOf((String) hmmrhit.get("archScore")),
185                   "HMMER3"));
186           addArchGroup(hitseq, arch);
187           alipp.setScore(Double.valueOf("" + dhit.get("bitscore")));
188           alipp.adjustForAlignment();
189           resultAl.addAnnotation(pval);
190           resultAl.addAnnotation(eval);
191           resultAl.addAnnotation(alipp);
192           alipp.validateRangeAndDisplay();
193         }
194       }
195       // look for other sequences represented by this hit and create rep groups
196       // could be in "pdbs", or ..
197       addRedundantSeqGroup(firsthit, alisqfrom, alisqto,
198               (JSONArray) hmmrhit.get("seqs"), true);
199     }
200   }
201
202   /**
203    * series of operations to perform for the viewpanel associated with the
204    * alignment
205    */
206   private List<Runnable> viewOps = new ArrayList<Runnable>();
207
208   public void updateView(AlignmentViewport view)
209   {
210     viewAl = view;
211     for (Runnable op : viewOps)
212     {
213       op.run();
214     }
215   }
216
217   private void addRedundantSeqGroup(final SequenceI firsthit,
218           long alisqfrom, long alisqto, JSONArray others, boolean justDelete)
219   {
220     if (others != null)
221     {
222       final SequenceGroup repgroup = new SequenceGroup();
223       repgroup.setSeqrep(firsthit);
224       repgroup.addOrRemove(firsthit, false);
225       repgroup.setStartRes(0);
226       repgroup.setEndRes(resultAl.getWidth() - 1);
227       for (Object otherseq : others.toArray(new JSONObject[0]))
228       {
229         String repseq = (String) ((JSONObject) otherseq).get("dn");
230         SequenceI[] other = resultAl.findSequenceMatch(repseq);
231         if (other != null && other.length > 0)
232         {
233           if (justDelete)
234           {
235             for (SequenceI oth : other)
236             {
237               resultAl.deleteSequence(oth);
238             }
239             ;
240           }
241           else
242           {
243             int ofound = 0;
244             for (SequenceI oth : other)
245             {
246               if (oth.getStart() == alisqfrom && oth.getEnd() == alisqto)
247               {
248                 ofound++;
249                 repgroup.addSequence(oth, false);
250               }
251             }
252             if (ofound == 0)
253             {
254               System.err.println("Warn - no match for redundant hit "
255                       + repseq + "/" + alisqfrom + "-" + alisqto);
256             }
257             if (ofound > 1)
258             {
259               System.err
260                       .println("Warn - multiple matches for redundant hit "
261                               + repseq + "/" + alisqfrom + "-" + alisqto);
262             }
263           }
264         }
265       }
266       if (repgroup.getSequences().size() > 1)
267       {
268         // queue a hide operation
269         final HmmerJSONProcessor me = this;
270         viewOps.add(new Runnable()
271         {
272           @Override
273           public void run()
274           {
275             me.viewAl.hideRepSequences(firsthit, repgroup);
276           }
277         });
278       }
279     }
280   }
281
282   Map<String, SequenceGroup> groups = new HashMap<String, SequenceGroup>();
283
284   private void addArchGroup(SequenceI seqToAdd, String groupNam)
285   {
286     SequenceGroup sg = groups.get(groupNam);
287     if (sg == null)
288     {
289       sg = new SequenceGroup();
290       sg.setName(groupNam);
291       sg.addSequence(seqToAdd, false);
292       sg.setStartRes(0);
293       sg.setEndRes(resultAl.getWidth() - 1);
294       groups.put(groupNam, sg);
295       resultAl.addGroup(sg);
296     }
297     else
298     {
299       sg.addSequence(seqToAdd, false);
300     }
301   }
302
303   private AlignmentAnnotation parsePosteriorProb(String ppline)
304   {
305     Annotation[] ae = new Annotation[ppline.length()];
306     int spos = 0;
307     for (int i = 0, iSize = ppline.length(); i < iSize; i++)
308     {
309       char pp = ppline.charAt(i);
310       if (pp == '*')
311       {
312         ae[spos++] = new Annotation(10f);
313       }
314       else
315       {
316         if (pp >= '0' && pp <= '9')
317         {
318           ae[spos++] = new Annotation(Integer.valueOf("" + pp));
319         }
320       }
321     }
322     AlignmentAnnotation pprob = new AlignmentAnnotation(
323             "Posterior Probability",
324             "Likelihood of HMM fit at each hit position.", ae);
325     pprob.graph = AlignmentAnnotation.BAR_GRAPH;
326     pprob.visible = false;
327     return pprob;
328   }
329 }