Uses RemoveGapsCommand
[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 \r
137           /////\r
138           //Uses RemoveGapsCommand\r
139           /////\r
140           new jalview.commands.RemoveGapsCommand("Remove Gaps",\r
141                                                  new SequenceI[] {sqs[msaIndex]},\r
142                                                  gc);\r
143 \r
144           SequenceI profileseq=al.getSequenceAt(FirstSeq);\r
145           profileseq.setSequence(sqs[msaIndex].getSequence());\r
146         }\r
147 \r
148         if (!jalview.analysis.SeqsetUtils.SeqCharacterUnhash(\r
149             al.getSequenceAt(FirstSeq), SequenceInfo))\r
150         {\r
151           throw (new Exception(\r
152           "Couldn't recover sequence properties for JNet Query sequence!"));\r
153         } else {\r
154           al.setDataset(null);\r
155           jalview.io.JnetAnnotationMaker.add_annotation(prediction, al, FirstSeq,\r
156               true, predMap);\r
157           SequenceI profileseq=al.getSequenceAt(0); // this includes any gaps.\r
158           alignToProfileSeq(al, profileseq);\r
159           if (predMap!=null) {\r
160             // Adjust input view for gaps\r
161             // propagate insertions into profile\r
162             alcsel=propagateInsertions(profileseq, al, input);\r
163           }\r
164         }\r
165       }\r
166       return new Object[] { al, alcsel}; // , FirstSeq, noMsa};\r
167     }\r
168     /**\r
169      * 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
170      * @param al\r
171      * @param profileseq\r
172      */\r
173     private void alignToProfileSeq(Alignment al, SequenceI profileseq) {\r
174       char gc = al.getGapCharacter();\r
175       int[] gapMap = profileseq.gapMap();\r
176       // insert gaps into profile\r
177       for (int lp=0,r=0; r<gapMap.length; r++) {\r
178         if (gapMap[r]-lp>1) {\r
179           StringBuffer sb=new StringBuffer();\r
180           for (int s=0, ns=gapMap[r]-lp; s<ns; s++) {\r
181             sb.append(gc);\r
182           }\r
183           for (int s=1,ns=al.getHeight(); s<ns; s++) {\r
184             String sq = al.getSequenceAt(s).getSequence();\r
185             int diff=gapMap[r]-sq.length();\r
186             if (diff>0) {\r
187               // pad gaps\r
188               sq=sq+sb;\r
189               while ((diff=gapMap[r]-sq.length())>0) {\r
190                 sq=sq+((diff>=sb.length()) ? sb.toString() : sb.substring(0, diff));\r
191               }\r
192               al.getSequenceAt(s).setSequence(sq);\r
193             } else {\r
194               al.getSequenceAt(s).setSequence(sq.substring(0,gapMap[r])+sb.toString()+sq.substring(gapMap[r]));\r
195             }\r
196           }\r
197         }\r
198         lp=gapMap[r];\r
199       }\r
200     }\r
201 \r
202     /**\r
203      * Add gaps into the sequences aligned to profileseq under the given AlignmentView\r
204      * @param profileseq\r
205      * @param al\r
206      * @param input\r
207      */\r
208     private ColumnSelection propagateInsertions(SequenceI profileseq, Alignment al, AlignmentView input) {\r
209       char gc = al.getGapCharacter();\r
210       Object[] alandcolsel = input.getAlignmentAndColumnSelection(gc);\r
211       ColumnSelection nview = (ColumnSelection) alandcolsel[1];\r
212       SequenceI origseq;\r
213       nview.pruneDeletions(ShiftList.parseMap((origseq=((SequenceI[]) alandcolsel[0])[0]).gapMap())); // recover original prediction sequence's mapping to view.\r
214       int[] viscontigs=nview.getVisibleContigs(0, profileseq.getLength());\r
215       int spos=0;\r
216       int offset=0;\r
217       //  input.pruneDeletions(ShiftList.parseMap(((SequenceI[]) alandcolsel[0])[0].gapMap()))\r
218       // add profile to visible contigs\r
219       for (int v=0; v<viscontigs.length; v+=2) {\r
220         if (viscontigs[v]>spos) {\r
221           StringBuffer sb=new StringBuffer();\r
222           for (int s=0, ns=viscontigs[v]-spos; s<ns; s++) {\r
223             sb.append(gc);\r
224           }\r
225           for (int s=0,ns=al.getHeight(); s<ns; s++) {\r
226             SequenceI sqobj = al.getSequenceAt(s);\r
227             if (sqobj!=profileseq) {\r
228               String sq = al.getSequenceAt(s).getSequence();\r
229               if (sq.length()<=spos+offset) {\r
230                 // pad sequence\r
231                 int diff=spos+offset-sq.length()-1;\r
232                 if (diff>0) {\r
233                   // pad gaps\r
234                   sq=sq+sb;\r
235                   while ((diff=spos+offset-sq.length()-1)>0) {\r
236                     sq=sq+((diff>=sb.length()) ? sb.toString() : sb.substring(0, diff));\r
237                   }\r
238                 }\r
239                 sq+=sb.toString();\r
240               } else {\r
241                 al.getSequenceAt(s).setSequence(sq.substring(0,spos+offset)+sb.toString()+sq.substring(spos+offset));\r
242               }\r
243             }\r
244           }\r
245           //offset+=sb.length();\r
246         }\r
247         spos = viscontigs[v+1]+1;\r
248       }\r
249       if ((offset+spos)<profileseq.getLength()) {\r
250         StringBuffer sb=new StringBuffer();\r
251         for (int s=0, ns=profileseq.getLength()-spos-offset; s<ns; s++) {\r
252           sb.append(gc);\r
253         }\r
254         for (int s=1,ns=al.getHeight(); s<ns; s++) {\r
255           String sq = al.getSequenceAt(s).getSequence();\r
256           // pad sequence\r
257           int diff=origseq.getLength()-sq.length();\r
258           while (diff>0) {\r
259             sq=sq+((diff>=sb.length()) ? sb.toString() : sb.substring(0, diff));\r
260             diff=origseq.getLength()-sq.length();\r
261           }\r
262         }\r
263       }\r
264       return nview;\r
265     }\r
266 \r
267     public JPredJob(Hashtable SequenceInfo, SequenceI seq, int[] delMap)\r
268     {\r
269       super();\r
270       this.predMap = delMap;\r
271       String sq = AlignSeq.extractGaps(Comparison.GapChars, seq.getSequence());\r
272       if (sq.length() >= 20)\r
273       {\r
274         this.SequenceInfo = SequenceInfo;\r
275         sequence = new vamsas.objects.simple.Sequence();\r
276         sequence.setId(seq.getName());\r
277         sequence.setSeq(sq);\r
278       }\r
279     }\r
280 \r
281     public JPredJob(Hashtable SequenceInfo, SequenceI[] msf, int[] delMap)\r
282     {\r
283       this(SequenceInfo, msf[0], delMap);\r
284       if (sequence != null)\r
285       {\r
286         if (msf.length > 1)\r
287         {\r
288           msa = new vamsas.objects.simple.Msfalignment();\r
289           jalview.io.PileUpfile pileup = new jalview.io.PileUpfile();\r
290           msa.setMsf(pileup.print(msf));\r
291         }\r
292       }\r
293     }\r
294   }\r
295   ext.vamsas.Jpred server;\r
296   String altitle = "";\r
297   JPredThread(WebserviceInfo wsinfo, String altitle, ext.vamsas.Jpred server, String wsurl, AlignmentView alview, AlignFrame alframe) {\r
298     super();\r
299     this.altitle = altitle;\r
300     this.server = server;\r
301     this.wsInfo = wsinfo;\r
302     this.input = alview;\r
303     this.alignFrame = alframe;\r
304     WsUrl = wsurl;\r
305   }\r
306 \r
307 \r
308   JPredThread(WebserviceInfo wsinfo, String altitle, ext.vamsas.Jpred server, String wsurl, Hashtable SequenceInfo,SequenceI seq, int[] delMap, AlignmentView alview, AlignFrame alframe)\r
309   {\r
310     this(wsinfo, altitle, server,wsurl, alview, alframe);\r
311     JPredJob job = new JPredJob(SequenceInfo, seq, delMap);\r
312     if (job.hasValidInput())\r
313     {\r
314       OutputHeader = wsInfo.getProgressText();\r
315       jobs = new WSJob[]\r
316                        {\r
317           job};\r
318       job.jobnum = 0;\r
319     }\r
320   }\r
321 \r
322   JPredThread(WebserviceInfo wsinfo, String altitle, ext.vamsas.Jpred server, Hashtable SequenceInfo, SequenceI[] msf, int[] delMap, AlignmentView alview, AlignFrame alframe, String wsurl)\r
323   {\r
324     this(wsinfo, altitle, server,wsurl, alview, alframe);\r
325     JPredJob job = new JPredJob(SequenceInfo, msf, delMap);\r
326     if (job.hasValidInput())\r
327     {\r
328       jobs = new WSJob[]\r
329                        {\r
330           job};\r
331       OutputHeader = wsInfo.getProgressText();\r
332       job.jobnum = 0;\r
333     }\r
334   }\r
335   void StartJob(WSJob j)\r
336   {\r
337     if (! (j instanceof JPredJob))\r
338     {\r
339       throw new Error("Implementation error - StartJob(JpredJob) called on " +\r
340           j.getClass());\r
341     }\r
342     try\r
343     {\r
344       JPredJob job = (JPredJob) j;\r
345       if (job.msa != null)\r
346       {\r
347         job.jobId = server.predictOnMsa(job.msa);\r
348       }\r
349       else\r
350         if (job.sequence!=null)\r
351         {\r
352           job.jobId = server.predict(job.sequence); // debug like : job.jobId = "/jobs/www-jpred/jp_Yatat29";//\r
353         }\r
354 \r
355       if (job.jobId != null)\r
356       {\r
357         if (job.jobId.startsWith("Broken"))\r
358         {\r
359           job.result = (vamsas.objects.simple.Result)new JpredResult();\r
360           job.result.setInvalid(true);\r
361           job.result.setStatus("Submission " + job.jobId);\r
362         }\r
363         else\r
364         {\r
365           job.submitted = true;\r
366           job.subjobComplete = false;\r
367           Cache.log.info(WsUrl + " Job Id '" + job.jobId + "'");\r
368         }\r
369       }\r
370       else\r
371       {\r
372         throw new Exception("Server timed out - try again later\n");\r
373       }\r
374     }\r
375     catch (Exception e)\r
376     {\r
377       if (e.getMessage().indexOf("Exception") > -1)\r
378       {\r
379         wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_SERVERERROR);\r
380         wsInfo.setProgressText(j.jobnum,\r
381             "Failed to submit the prediction. (Just close the window)\n"\r
382             +\r
383         "It is most likely that there is a problem with the server.\n");\r
384         System.err.println(\r
385             "JPredWS Client: Failed to submit the prediction. Quite possibly because of a server error - see below)\n" +\r
386             e.getMessage() + "\n");\r
387 \r
388         jalview.bin.Cache.log.warn("Server Exception", e);\r
389       }\r
390       else\r
391       {\r
392         wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR);\r
393         // JBPNote - this could be a popup informing the user of the problem.\r
394         wsInfo.appendProgressText(j.jobnum,\r
395             "Failed to submit the prediction:\n"\r
396             + e.getMessage() +\r
397             wsInfo.getProgressText());\r
398 \r
399         jalview.bin.Cache.log.debug("Failed Submission of job " + j.jobnum, e);\r
400 \r
401       }\r
402       j.allowedServerExceptions = -1;\r
403       j.subjobComplete = true;\r
404     }\r
405   }\r
406 \r
407   void parseResult()\r
408   {\r
409     int results = 0; // number of result sets received\r
410     JobStateSummary finalState = new JobStateSummary();\r
411     try\r
412     {\r
413       for (int j = 0; j < jobs.length; j++)\r
414       {\r
415         finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);\r
416         if (jobs[j].submitted && jobs[j].subjobComplete && jobs[j].hasResults())\r
417         {\r
418           results++;\r
419         }\r
420       }\r
421     }\r
422     catch (Exception ex)\r
423     {\r
424 \r
425       Cache.log.error("Unexpected exception when processing results for " +\r
426           altitle, ex);\r
427       wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);\r
428     }\r
429     if (results > 0)\r
430     {\r
431       wsInfo.showResultsNewFrame\r
432       .addActionListener(new java.awt.event.ActionListener()\r
433       {\r
434         public void actionPerformed(\r
435             java.awt.event.ActionEvent evt)\r
436         {\r
437           displayResults(true);\r
438         }\r
439       });\r
440       wsInfo.mergeResults\r
441       .addActionListener(new java.awt.event.ActionListener()\r
442       {\r
443         public void actionPerformed(\r
444             java.awt.event.ActionEvent evt)\r
445         {\r
446           displayResults(false);\r
447         }\r
448       });\r
449       wsInfo.setResultsReady();\r
450     }\r
451     else\r
452     {\r
453       wsInfo.setFinishedNoResults();\r
454     }\r
455   }\r
456 \r
457   void displayResults(boolean newWindow)\r
458   {\r
459     // TODO: cope with multiple subjobs.\r
460     if (jobs != null)\r
461     {\r
462       Object[] res = null;\r
463       boolean msa=false;\r
464       for (int jn = 0; jn < jobs.length; jn++)\r
465       {\r
466         Object[] jobres = null;\r
467         JPredJob j = (JPredJob) jobs[jn];\r
468 \r
469         if (j.hasResults())\r
470         {\r
471           // hack - we only deal with all single seuqence predictions or all profile predictions\r
472           msa = (j.msa!=null) ? true : msa;\r
473           try\r
474           {\r
475             jalview.bin.Cache.log.debug("Parsing output of job " + jn);\r
476             jobres = j.getResultSet();\r
477             jalview.bin.Cache.log.debug("Finished parsing output.");\r
478             if (jobs.length==1)\r
479               res = jobres;\r
480             else {\r
481               // do merge with other job results\r
482               throw new Error("Multiple JNet subjob merging not yet implemented.");\r
483             }\r
484           }\r
485           catch (Exception e)\r
486           {\r
487             jalview.bin.Cache.log.error(\r
488                 "JNet Client: JPred Annotation Parse Error",\r
489                 e);\r
490             wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR);\r
491             wsInfo.appendProgressText(j.jobnum,\r
492                 OutputHeader + "\n" +\r
493                 j.result.getStatus() +\r
494                 "\nInvalid JNet job result data!\n" +\r
495                 e.getMessage());\r
496             j.result.setBroken(true);\r
497           }\r
498         }\r
499       }\r
500 \r
501       if (res != null)\r
502       {\r
503         if (newWindow)\r
504         {\r
505           AlignFrame af;\r
506           if (input==null) {\r
507             if (res[1]!=null) {\r
508               af = new AlignFrame((Alignment)res[0], (ColumnSelection) res[1], AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);\r
509             } else {\r
510               af = new AlignFrame((Alignment)res[0], AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);\r
511             }\r
512           } else {\r
513             /*java.lang.Object[] alandcolsel = input.getAlignmentAndColumnSelection(alignFrame.getViewport().getGapCharacter());\r
514 \r
515             if (((SequenceI[])alandcolsel[0])[0].getLength()!=res.getWidth()) {\r
516               if (msa) {\r
517                 throw new Error("Implementation Error! ColumnSelection from input alignment will not map to result alignment!");\r
518               }\r
519             }\r
520             if (!msa) {\r
521               // update hidden regions to account for loss of gaps in profile. - if any\r
522               // gapMap returns insert list, interpreted as delete list by pruneDeletions\r
523               //((ColumnSelection) alandcolsel[1]).pruneDeletions(ShiftList.parseMap(((SequenceI[]) alandcolsel[0])[0].gapMap()));\r
524             }*/\r
525 \r
526             af = new AlignFrame((Alignment) res[0], (ColumnSelection) res[1],AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);\r
527           }\r
528           Desktop.addInternalFrame(af, altitle,\r
529               AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);\r
530         }\r
531         else\r
532         {\r
533           Cache.log.info("Append results onto existing alignment.");\r
534         }\r
535       }\r
536     }\r
537   }\r
538   void pollJob(WSJob job)\r
539   throws Exception\r
540   {\r
541     job.result = server.getresult(job.jobId);\r
542   }\r
543   public boolean isCancellable()\r
544   {\r
545     return false;\r
546   }\r
547 \r
548   public void cancelJob()\r
549   {\r
550     throw new Error("Implementation error!");\r
551   }\r
552 \r
553   public boolean canMergeResults()\r
554   {\r
555     return false;\r
556   }\r
557 \r
558 }\r
559 \r
560 \r