Merge branch 'Jalview-JS/develop' into merge_js_develop
[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,
185                   (hmmrhit.get("archScore") != null ? Integer
186                           .valueOf((String) hmmrhit.get("archScore")) : 0f),
187                   "HMMER3"));
188           addArchGroup(hitseq, arch);
189           alipp.setScore(Double.valueOf("" + dhit.get("bitscore")));
190           alipp.adjustForAlignment();
191           resultAl.addAnnotation(pval);
192           resultAl.addAnnotation(eval);
193           resultAl.addAnnotation(alipp);
194           alipp.validateRangeAndDisplay();
195         }
196       }
197       // look for other sequences represented by this hit and create rep groups
198       // could be in "pdbs", or ..
199       addRedundantSeqGroup(firsthit, alisqfrom, alisqto,
200               (JSONArray) hmmrhit.get("seqs"), true);
201     }
202   }
203
204   /**
205    * series of operations to perform for the viewpanel associated with the
206    * alignment
207    */
208   private List<Runnable> viewOps = new ArrayList<Runnable>();
209
210   public void updateView(AlignmentViewport view)
211   {
212     viewAl = view;
213     for (Runnable op : viewOps)
214     {
215       op.run();
216     }
217   }
218
219   private void addRedundantSeqGroup(final SequenceI firsthit,
220           long alisqfrom, long alisqto, JSONArray others, boolean justDelete)
221   {
222     if (others != null)
223     {
224       final SequenceGroup repgroup = new SequenceGroup();
225       repgroup.setSeqrep(firsthit);
226       repgroup.addOrRemove(firsthit, false);
227       repgroup.setStartRes(0);
228       repgroup.setEndRes(resultAl.getWidth() - 1);
229       for (Object otherseq : others.toArray(new JSONObject[0]))
230       {
231         String repseq = (String) ((JSONObject) otherseq).get("dn");
232         SequenceI[] other = resultAl.findSequenceMatch(repseq);
233         if (other != null && other.length > 0)
234         {
235           if (justDelete)
236           {
237             for (SequenceI oth : other)
238             {
239               resultAl.deleteSequence(oth);
240             }
241             ;
242           }
243           else
244           {
245             int ofound = 0;
246             for (SequenceI oth : other)
247             {
248               if (oth.getStart() == alisqfrom && oth.getEnd() == alisqto)
249               {
250                 ofound++;
251                 repgroup.addSequence(oth, false);
252               }
253             }
254             if (ofound == 0)
255             {
256               System.err.println("Warn - no match for redundant hit "
257                       + repseq + "/" + alisqfrom + "-" + alisqto);
258             }
259             if (ofound > 1)
260             {
261               System.err
262                       .println("Warn - multiple matches for redundant hit "
263                               + repseq + "/" + alisqfrom + "-" + alisqto);
264             }
265           }
266         }
267       }
268       if (repgroup.getSequences().size() > 1)
269       {
270         // queue a hide operation
271         final HmmerJSONProcessor me = this;
272         viewOps.add(new Runnable()
273         {
274           @Override
275           public void run()
276           {
277             me.viewAl.hideRepSequences(firsthit, repgroup);
278           }
279         });
280       }
281     }
282   }
283
284   Map<String, SequenceGroup> groups = new HashMap<String, SequenceGroup>();
285
286   private void addArchGroup(SequenceI seqToAdd, String groupNam)
287   {
288     SequenceGroup sg = groups.get(groupNam);
289     if (sg == null)
290     {
291       sg = new SequenceGroup();
292       sg.setName(groupNam);
293       sg.addSequence(seqToAdd, false);
294       sg.setStartRes(0);
295       sg.setEndRes(resultAl.getWidth() - 1);
296       groups.put(groupNam, sg);
297       resultAl.addGroup(sg);
298     }
299     else
300     {
301       sg.addSequence(seqToAdd, false);
302     }
303   }
304
305   private AlignmentAnnotation parsePosteriorProb(String ppline)
306   {
307     Annotation[] ae = new Annotation[ppline.length()];
308     int spos = 0;
309     for (int i = 0, iSize = ppline.length(); i < iSize; i++)
310     {
311       char pp = ppline.charAt(i);
312       if (pp == '*')
313       {
314         ae[spos++] = new Annotation(10f);
315       }
316       else
317       {
318         if (pp >= '0' && pp <= '9')
319         {
320           ae[spos++] = new Annotation(Integer.valueOf("" + pp));
321         }
322       }
323     }
324     AlignmentAnnotation pprob = new AlignmentAnnotation(
325             "Posterior Probability",
326             "Likelihood of HMM fit at each hit position.", ae);
327     pprob.graph = AlignmentAnnotation.BAR_GRAPH;
328     pprob.visible = false;
329     return pprob;
330   }
331 }