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