Added filter so that input to a JPred job is *solely* the visible residues, and the...
[jalview.git] / src / jalview / ws / JPredThread.java
1 package jalview.ws;\r
2 \r
3 import java.util.*;\r
4 \r
5 import javax.swing.*;\r
6 \r
7 import vamsas.objects.simple.JpredResult;\r
8 \r
9 import ext.vamsas.*;\r
10 import jalview.analysis.*;\r
11 import jalview.bin.*;\r
12 import jalview.datamodel.*;\r
13 import jalview.gui.*;\r
14 import jalview.io.*;\r
15 import jalview.util.*;\r
16 import jalview.ws.WSThread.*;\r
17 \r
18 class JPredThread\r
19 extends WSThread\r
20 implements WSClientI\r
21 {\r
22   // TODO: put mapping between JPredJob input and input data here - JNetAnnotation adding is done after result parsing.\r
23   class JPredJob\r
24   extends WSThread.WSJob\r
25   {\r
26     // TODO: make JPredJob deal only with what was sent to and received from a JNet service\r
27     int[] predMap=null; // mapping from sequence(i) to the original sequence(predMap[i]) being predicted on\r
28     vamsas.objects.simple.Sequence sequence;\r
29     vamsas.objects.simple.Msfalignment msa;\r
30     java.util.Hashtable SequenceInfo = null;\r
31     int msaIndex=0; // the position of the original sequence in the array of Sequences in the input object that this job holds a prediction for\r
32     /**\r
33      *\r
34      * @return true if getResultSet will return a valid alignment and prediction result.\r
35      */\r
36     public boolean hasResults()\r
37     {\r
38       if (subjobComplete && result != null && result.isFinished()\r
39           && ( (JpredResult) result).getPredfile() != null &&\r
40           ( (JpredResult) result).getAligfile() != null)\r
41       {\r
42         return true;\r
43       }\r
44       return false;\r
45     }\r
46 \r
47     boolean hasValidInput()\r
48     {\r
49       if (sequence != null)\r
50       {\r
51         return true;\r
52       }\r
53       return false;\r
54     }\r
55     /**\r
56      * \r
57      * @return null or Object[] { annotated alignment for this prediction, ColumnSelection for this prediction} or null if no results available.\r
58      * @throws Exception\r
59      */\r
60     public Object[] getResultSet()\r
61     throws Exception\r
62     {\r
63       if (result == null || !result.isFinished())\r
64       {\r
65         return null;\r
66       }\r
67       Alignment al = null;\r
68       ColumnSelection alcsel=null;\r
69       int FirstSeq = -1; // the position of the query sequence in Alignment al\r
70 \r
71       JpredResult result = (JpredResult)this.result;\r
72 \r
73       jalview.bin.Cache.log.debug("Parsing output from JNet job.");\r
74       // JPredFile prediction = new JPredFile("C:/JalviewX/files/jpred.txt", "File");\r
75       jalview.io.JPredFile prediction = new jalview.io.JPredFile(result.\r
76           getPredfile(),\r
77       "Paste");\r
78       SequenceI[] preds = prediction.getSeqsAsArray();\r
79       jalview.bin.Cache.log.debug("Got prediction profile.");\r
80 \r
81       if ( (this.msa != null) && (result.getAligfile() != null))\r
82       {\r
83         jalview.bin.Cache.log.debug("Getting associated alignment.");\r
84         // we ignore the returned alignment if we only predicted on a single sequence\r
85         String format = new jalview.io.IdentifyFile().Identify(result.\r
86             getAligfile(),\r
87         "Paste");\r
88 \r
89         if (jalview.io.FormatAdapter.isValidFormat(format))\r
90         {\r
91           SequenceI sqs[];\r
92           if (predMap!=null) {\r
93             Object[] alandcolsel = input.getAlignmentAndColumnSelection(alignFrame.getViewport().getGapCharacter());\r
94             sqs = (SequenceI[]) alandcolsel[0];\r
95             al = new Alignment(sqs);\r
96             alcsel=(ColumnSelection) alandcolsel[1];\r
97           } else {\r
98             al = new Alignment(new FormatAdapter().readFile(result.getAligfile(),\r
99                 "Paste", format));\r
100             sqs = new SequenceI[al.getHeight()];\r
101 \r
102             for (int i = 0, j = al.getHeight(); i < j; i++)\r
103             {\r
104               sqs[i] = al.getSequenceAt(i);\r
105             } \r
106             if (!jalview.analysis.SeqsetUtils.deuniquify( (Hashtable)\r
107                 SequenceInfo, sqs))\r
108             {\r
109               throw (new Exception(\r
110               "Couldn't recover sequence properties for alignment."));\r
111             }\r
112           }\r
113           FirstSeq = 0;\r
114           al.setDataset(null);\r
115 \r
116           jalview.io.JnetAnnotationMaker.add_annotation(prediction, al, FirstSeq,\r
117               false,predMap);\r
118 \r
119         }\r
120         else\r
121         {\r
122           throw (new Exception(\r
123               "Unknown format "+format+" for file : \n" +\r
124               result.getAligfile()));\r
125         }\r
126       }\r
127       else\r
128       {\r
129         al = new Alignment(preds);\r
130         FirstSeq = prediction.getQuerySeqPosition();\r
131         if (predMap!=null) {\r
132           char gc = alignFrame.getViewport().getGapCharacter();\r
133           SequenceI[] sqs = (SequenceI[]) ((java.lang.Object[]) input.getAlignmentAndColumnSelection(gc))[0];\r
134           if (this.msaIndex>=sqs.length)\r
135             throw new Error("Implementation Error! Invalid msaIndex for JPredJob on parent MSA input object!");\r
136           sqs[msaIndex].removeGaps();\r
137           SequenceI profileseq=al.getSequenceAt(FirstSeq);\r
138           profileseq.setSequence(sqs[msaIndex].getSequence());\r
139         }\r
140 \r
141         if (!jalview.analysis.SeqsetUtils.SeqCharacterUnhash(\r
142             al.getSequenceAt(FirstSeq), SequenceInfo))\r
143         {\r
144           throw (new Exception(\r
145           "Couldn't recover sequence properties for JNet Query sequence!"));\r
146         } else {\r
147           al.setDataset(null);\r
148           jalview.io.JnetAnnotationMaker.add_annotation(prediction, al, FirstSeq,\r
149               true, predMap);\r
150           SequenceI profileseq=al.getSequenceAt(0); // this includes any gaps.\r
151           alignToProfileSeq(al, profileseq);\r
152           if (predMap!=null) {\r
153             // Adjust input view for gaps\r
154             // propagate insertions into profile\r
155             alcsel=propagateInsertions(profileseq, al, input);\r
156           }\r
157         }\r
158       }\r
159       return new Object[] { al, alcsel}; // , FirstSeq, noMsa};\r
160     }\r
161     /**\r
162      * Given an alignment where all other sequences except profileseq are aligned to the ungapped profileseq, insert gaps in the other sequences to realign them with the residues in profileseq\r
163      * @param al\r
164      * @param profileseq\r
165      */\r
166     private void alignToProfileSeq(Alignment al, SequenceI profileseq) {\r
167       char gc = al.getGapCharacter();\r
168       int[] gapMap = profileseq.gapMap();\r
169       // insert gaps into profile\r
170       for (int lp=0,r=0; r<gapMap.length; r++) {\r
171         if (gapMap[r]-lp>1) {\r
172           StringBuffer sb=new StringBuffer();\r
173           for (int s=0, ns=gapMap[r]-lp; s<ns; s++) {\r
174             sb.append(gc);\r
175           }\r
176           for (int s=1,ns=al.getHeight(); s<ns; s++) {\r
177             String sq = al.getSequenceAt(s).getSequence();\r
178             int diff=gapMap[r]-sq.length();\r
179             if (diff>0) {\r
180               // pad gaps\r
181               sq=sq+sb;\r
182               while ((diff=gapMap[r]-sq.length())>0) {\r
183                 sq=sq+((diff>=sb.length()) ? sb.toString() : sb.substring(0, diff));\r
184               }\r
185               al.getSequenceAt(s).setSequence(sq);\r
186             } else {\r
187               al.getSequenceAt(s).setSequence(sq.substring(0,gapMap[r])+sb.toString()+sq.substring(gapMap[r]));\r
188             }\r
189           }\r
190         }\r
191         lp=gapMap[r];\r
192       }\r
193     }\r
194 \r
195     /**\r
196      * Add gaps into the sequences aligned to profileseq under the given AlignmentView\r
197      * @param profileseq\r
198      * @param al\r
199      * @param input\r
200      */\r
201     private ColumnSelection propagateInsertions(SequenceI profileseq, Alignment al, AlignmentView input) {\r
202       char gc = al.getGapCharacter();\r
203       Object[] alandcolsel = input.getAlignmentAndColumnSelection(gc);\r
204       ColumnSelection nview = (ColumnSelection) alandcolsel[1];\r
205       SequenceI origseq;\r
206       nview.pruneDeletions(ShiftList.parseMap((origseq=((SequenceI[]) alandcolsel[0])[0]).gapMap())); // recover original prediction sequence's mapping to view.\r
207       int[] viscontigs=nview.getVisibleContigs(0, profileseq.getLength());\r
208       int spos=0;\r
209       int offset=0;\r
210       //  input.pruneDeletions(ShiftList.parseMap(((SequenceI[]) alandcolsel[0])[0].gapMap()))\r
211       // add profile to visible contigs\r
212       for (int v=0; v<viscontigs.length; v+=2) {\r
213         if (viscontigs[v]>spos) {\r
214           StringBuffer sb=new StringBuffer();\r
215           for (int s=0, ns=viscontigs[v]-spos; s<ns; s++) {\r
216             sb.append(gc);\r
217           }\r
218           for (int s=0,ns=al.getHeight(); s<ns; s++) {\r
219             SequenceI sqobj = al.getSequenceAt(s);\r
220             if (sqobj!=profileseq) {\r
221               String sq = al.getSequenceAt(s).getSequence();\r
222               if (sq.length()<=spos+offset) {\r
223                 // pad sequence\r
224                 int diff=spos+offset-sq.length()-1;\r
225                 if (diff>0) {\r
226                   // pad gaps\r
227                   sq=sq+sb;\r
228                   while ((diff=spos+offset-sq.length()-1)>0) {\r
229                     sq=sq+((diff>=sb.length()) ? sb.toString() : sb.substring(0, diff));\r
230                   }\r
231                 }\r
232                 sq+=sb.toString();\r
233               } else {\r
234                 al.getSequenceAt(s).setSequence(sq.substring(0,spos+offset)+sb.toString()+sq.substring(spos+offset));\r
235               }\r
236             }\r
237           }\r
238           //offset+=sb.length();\r
239         }\r
240         spos = viscontigs[v+1]+1;\r
241       }\r
242       if ((offset+spos)<profileseq.getLength()) {\r
243         StringBuffer sb=new StringBuffer();\r
244         for (int s=0, ns=profileseq.getLength()-spos-offset; s<ns; s++) {\r
245           sb.append(gc);\r
246         }\r
247         for (int s=1,ns=al.getHeight(); s<ns; s++) {\r
248           String sq = al.getSequenceAt(s).getSequence();\r
249           // pad sequence\r
250           int diff=origseq.getLength()-sq.length();\r
251           while (diff>0) {\r
252             sq=sq+((diff>=sb.length()) ? sb.toString() : sb.substring(0, diff));\r
253             diff=origseq.getLength()-sq.length();            \r
254           }\r
255         }\r
256       }\r
257       return nview;\r
258     }\r
259 \r
260     public JPredJob(Hashtable SequenceInfo, SequenceI seq, int[] delMap)\r
261     {\r
262       super();\r
263       this.predMap = delMap;\r
264       String sq = AlignSeq.extractGaps(Comparison.GapChars, seq.getSequence());\r
265       if (sq.length() >= 20)\r
266       {\r
267         this.SequenceInfo = SequenceInfo;\r
268         sequence = new vamsas.objects.simple.Sequence();\r
269         sequence.setId(seq.getName());\r
270         sequence.setSeq(sq);\r
271       }\r
272     }\r
273 \r
274     public JPredJob(Hashtable SequenceInfo, SequenceI[] msf, int[] delMap)\r
275     {\r
276       this(SequenceInfo, msf[0], delMap);\r
277       if (sequence != null)\r
278       {\r
279         if (msf.length > 1)\r
280         {\r
281           msa = new vamsas.objects.simple.Msfalignment();\r
282           jalview.io.PileUpfile pileup = new jalview.io.PileUpfile();\r
283           msa.setMsf(pileup.print(msf));\r
284         }\r
285       }\r
286     }\r
287   }\r
288   ext.vamsas.Jpred server;\r
289   String altitle = "";\r
290   JPredThread(WebserviceInfo wsinfo, String altitle, ext.vamsas.Jpred server, String wsurl, AlignmentView alview, AlignFrame alframe) {\r
291     super();\r
292     this.altitle = altitle;\r
293     this.server = server;\r
294     this.wsInfo = wsinfo;\r
295     this.input = alview;\r
296     this.alignFrame = alframe;\r
297     WsUrl = wsurl;\r
298   }\r
299 \r
300 \r
301   JPredThread(WebserviceInfo wsinfo, String altitle, ext.vamsas.Jpred server, String wsurl, Hashtable SequenceInfo,SequenceI seq, int[] delMap, AlignmentView alview, AlignFrame alframe)\r
302   {\r
303     this(wsinfo, altitle, server,wsurl, alview, alframe);\r
304     JPredJob job = new JPredJob(SequenceInfo, seq, delMap);\r
305     if (job.hasValidInput())\r
306     {\r
307       OutputHeader = wsInfo.getProgressText();\r
308       jobs = new WSJob[]\r
309                        {\r
310           job};\r
311       job.jobnum = 0;\r
312     }\r
313   }\r
314 \r
315   JPredThread(WebserviceInfo wsinfo, String altitle, ext.vamsas.Jpred server, Hashtable SequenceInfo, SequenceI[] msf, int[] delMap, AlignmentView alview, AlignFrame alframe, String wsurl)\r
316   {\r
317     this(wsinfo, altitle, server,wsurl, alview, alframe);\r
318     JPredJob job = new JPredJob(SequenceInfo, msf, delMap);\r
319     if (job.hasValidInput())\r
320     {\r
321       jobs = new WSJob[]\r
322                        {\r
323           job};\r
324       OutputHeader = wsInfo.getProgressText();\r
325       job.jobnum = 0;\r
326     }\r
327   }\r
328   void StartJob(WSJob j)\r
329   {\r
330     if (! (j instanceof JPredJob))\r
331     {\r
332       throw new Error("Implementation error - StartJob(JpredJob) called on " +\r
333           j.getClass());\r
334     }\r
335     try\r
336     {\r
337       JPredJob job = (JPredJob) j;\r
338       if (job.msa != null)\r
339       {\r
340         job.jobId = server.predictOnMsa(job.msa);\r
341       }\r
342       else\r
343         if (job.sequence!=null)\r
344         {\r
345           job.jobId = server.predict(job.sequence); // debug like : job.jobId = "/jobs/www-jpred/jp_Yatat29";//\r
346         }\r
347 \r
348       if (job.jobId != null)\r
349       {\r
350         if (job.jobId.startsWith("Broken"))\r
351         {\r
352           job.result = (vamsas.objects.simple.Result)new JpredResult();\r
353           job.result.setInvalid(true);\r
354           job.result.setStatus("Submission " + job.jobId);\r
355         }\r
356         else\r
357         {\r
358           job.submitted = true;\r
359           job.subjobComplete = false;\r
360           Cache.log.info(WsUrl + " Job Id '" + job.jobId + "'");\r
361         }\r
362       }\r
363       else\r
364       {\r
365         throw new Exception("Server timed out - try again later\n");\r
366       }\r
367     }\r
368     catch (Exception e)\r
369     {\r
370       if (e.getMessage().indexOf("Exception") > -1)\r
371       {\r
372         wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_SERVERERROR);\r
373         wsInfo.setProgressText(j.jobnum,\r
374             "Failed to submit the prediction. (Just close the window)\n"\r
375             +\r
376         "It is most likely that there is a problem with the server.\n");\r
377         System.err.println(\r
378             "JPredWS Client: Failed to submit the prediction. Quite possibly because of a server error - see below)\n" +\r
379             e.getMessage() + "\n");\r
380 \r
381         jalview.bin.Cache.log.warn("Server Exception", e);\r
382       }\r
383       else\r
384       {\r
385         wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR);\r
386         // JBPNote - this could be a popup informing the user of the problem.\r
387         wsInfo.appendProgressText(j.jobnum,\r
388             "Failed to submit the prediction:\n"\r
389             + e.getMessage() +\r
390             wsInfo.getProgressText());\r
391 \r
392         jalview.bin.Cache.log.debug("Failed Submission of job " + j.jobnum, e);\r
393 \r
394       }\r
395       j.allowedServerExceptions = -1;\r
396       j.subjobComplete = true;\r
397     }\r
398   }\r
399 \r
400   void parseResult()\r
401   {\r
402     int results = 0; // number of result sets received\r
403     JobStateSummary finalState = new JobStateSummary();\r
404     try\r
405     {\r
406       for (int j = 0; j < jobs.length; j++)\r
407       {\r
408         finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);\r
409         if (jobs[j].submitted && jobs[j].subjobComplete && jobs[j].hasResults())\r
410         {\r
411           results++;\r
412         }\r
413       }\r
414     }\r
415     catch (Exception ex)\r
416     {\r
417 \r
418       Cache.log.error("Unexpected exception when processing results for " +\r
419           altitle, ex);\r
420       wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);\r
421     }\r
422     if (results > 0)\r
423     {\r
424       wsInfo.showResultsNewFrame\r
425       .addActionListener(new java.awt.event.ActionListener()\r
426       {\r
427         public void actionPerformed(\r
428             java.awt.event.ActionEvent evt)\r
429         {\r
430           displayResults(true);\r
431         }\r
432       });\r
433       wsInfo.mergeResults\r
434       .addActionListener(new java.awt.event.ActionListener()\r
435       {\r
436         public void actionPerformed(\r
437             java.awt.event.ActionEvent evt)\r
438         {\r
439           displayResults(false);\r
440         }\r
441       });\r
442       wsInfo.setResultsReady();\r
443     }\r
444     else\r
445     {\r
446       wsInfo.setFinishedNoResults();\r
447     }\r
448   }\r
449 \r
450   void displayResults(boolean newWindow)\r
451   {\r
452     // TODO: cope with multiple subjobs.\r
453     if (jobs != null)\r
454     {\r
455       Object[] res = null;\r
456       boolean msa=false;\r
457       for (int jn = 0; jn < jobs.length; jn++)\r
458       {\r
459         Object[] jobres = null;\r
460         JPredJob j = (JPredJob) jobs[jn];\r
461 \r
462         if (j.hasResults())\r
463         {\r
464           // hack - we only deal with all single seuqence predictions or all profile predictions\r
465           msa = (j.msa!=null) ? true : msa;\r
466           try\r
467           {\r
468             jalview.bin.Cache.log.debug("Parsing output of job " + jn);\r
469             jobres = j.getResultSet();\r
470             jalview.bin.Cache.log.debug("Finished parsing output.");\r
471             if (jobs.length==1)\r
472               res = jobres;\r
473             else {\r
474               // do merge with other job results\r
475               throw new Error("Multiple JNet subjob merging not yet implemented.");\r
476             }\r
477           }\r
478           catch (Exception e)\r
479           {\r
480             jalview.bin.Cache.log.error(\r
481                 "JNet Client: JPred Annotation Parse Error",\r
482                 e);\r
483             wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR);\r
484             wsInfo.appendProgressText(j.jobnum,\r
485                 OutputHeader + "\n" +\r
486                 j.result.getStatus() +\r
487                 "\nInvalid JNet job result data!\n" +\r
488                 e.getMessage());\r
489             j.result.setBroken(true);\r
490           }\r
491         }\r
492       }\r
493 \r
494       if (res != null)\r
495       {\r
496         if (newWindow)\r
497         {\r
498           AlignFrame af;\r
499           if (input==null) {\r
500             if (res[1]!=null) {\r
501               af = new AlignFrame((Alignment)res[0], (ColumnSelection) res[1], AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);\r
502             } else {\r
503               af = new AlignFrame((Alignment)res[0], AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);\r
504             }\r
505           } else {\r
506             /*java.lang.Object[] alandcolsel = input.getAlignmentAndColumnSelection(alignFrame.getViewport().getGapCharacter());\r
507 \r
508             if (((SequenceI[])alandcolsel[0])[0].getLength()!=res.getWidth()) {\r
509               if (msa) {\r
510                 throw new Error("Implementation Error! ColumnSelection from input alignment will not map to result alignment!");\r
511               } \r
512             }\r
513             if (!msa) {\r
514               // update hidden regions to account for loss of gaps in profile. - if any\r
515               // gapMap returns insert list, interpreted as delete list by pruneDeletions\r
516               //((ColumnSelection) alandcolsel[1]).pruneDeletions(ShiftList.parseMap(((SequenceI[]) alandcolsel[0])[0].gapMap()));\r
517             }*/\r
518             \r
519             af = new AlignFrame((Alignment) res[0], (ColumnSelection) res[1],AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);\r
520           }\r
521           Desktop.addInternalFrame(af, altitle,\r
522               AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);\r
523         }\r
524         else\r
525         {\r
526           Cache.log.info("Append results onto existing alignment.");\r
527         }\r
528       }\r
529     }\r
530   }\r
531   void pollJob(WSJob job)\r
532   throws Exception\r
533   {\r
534     job.result = server.getresult(job.jobId);\r
535   }\r
536   public boolean isCancellable()\r
537   {\r
538     return false;\r
539   }\r
540 \r
541   public void cancelJob()\r
542   {\r
543     throw new Error("Implementation error!");\r
544   }\r
545 \r
546   public boolean canMergeResults()\r
547   {\r
548     return false;\r
549   }\r
550 \r
551 }\r
552 \r
553 \r