Sequence is char []
[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].getSequenceAsString());\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).getSequenceAsString();\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).getSequenceAsString();\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).getSequenceAsString();\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,\r
272                                        seq.getSequenceAsString());\r
273       if (sq.length() >= 20)\r
274       {\r
275         this.SequenceInfo = SequenceInfo;\r
276         sequence = new vamsas.objects.simple.Sequence();\r
277         sequence.setId(seq.getName());\r
278         sequence.setSeq(sq);\r
279       }\r
280     }\r
281 \r
282     public JPredJob(Hashtable SequenceInfo, SequenceI[] msf, int[] delMap)\r
283     {\r
284       this(SequenceInfo, msf[0], delMap);\r
285       if (sequence != null)\r
286       {\r
287         if (msf.length > 1)\r
288         {\r
289           msa = new vamsas.objects.simple.Msfalignment();\r
290           jalview.io.PileUpfile pileup = new jalview.io.PileUpfile();\r
291           msa.setMsf(pileup.print(msf));\r
292         }\r
293       }\r
294     }\r
295   }\r
296   ext.vamsas.Jpred server;\r
297   String altitle = "";\r
298   JPredThread(WebserviceInfo wsinfo, String altitle, ext.vamsas.Jpred server, String wsurl, AlignmentView alview, AlignFrame alframe) {\r
299     super();\r
300     this.altitle = altitle;\r
301     this.server = server;\r
302     this.wsInfo = wsinfo;\r
303     this.input = alview;\r
304     this.alignFrame = alframe;\r
305     WsUrl = wsurl;\r
306   }\r
307 \r
308 \r
309   JPredThread(WebserviceInfo wsinfo, String altitle, ext.vamsas.Jpred server, String wsurl, Hashtable SequenceInfo,SequenceI seq, int[] delMap, AlignmentView alview, AlignFrame alframe)\r
310   {\r
311     this(wsinfo, altitle, server,wsurl, alview, alframe);\r
312     JPredJob job = new JPredJob(SequenceInfo, seq, delMap);\r
313     if (job.hasValidInput())\r
314     {\r
315       OutputHeader = wsInfo.getProgressText();\r
316       jobs = new WSJob[]\r
317                        {\r
318           job};\r
319       job.jobnum = 0;\r
320     }\r
321   }\r
322 \r
323   JPredThread(WebserviceInfo wsinfo, String altitle, ext.vamsas.Jpred server, Hashtable SequenceInfo, SequenceI[] msf, int[] delMap, AlignmentView alview, AlignFrame alframe, String wsurl)\r
324   {\r
325     this(wsinfo, altitle, server,wsurl, alview, alframe);\r
326     JPredJob job = new JPredJob(SequenceInfo, msf, delMap);\r
327     if (job.hasValidInput())\r
328     {\r
329       jobs = new WSJob[]\r
330                        {\r
331           job};\r
332       OutputHeader = wsInfo.getProgressText();\r
333       job.jobnum = 0;\r
334     }\r
335   }\r
336   void StartJob(WSJob j)\r
337   {\r
338     if (! (j instanceof JPredJob))\r
339     {\r
340       throw new Error("Implementation error - StartJob(JpredJob) called on " +\r
341           j.getClass());\r
342     }\r
343     try\r
344     {\r
345       JPredJob job = (JPredJob) j;\r
346       if (job.msa != null)\r
347       {\r
348         job.jobId = server.predictOnMsa(job.msa);\r
349       }\r
350       else\r
351         if (job.sequence!=null)\r
352         {\r
353           job.jobId = server.predict(job.sequence); // debug like : job.jobId = "/jobs/www-jpred/jp_Yatat29";//\r
354         }\r
355 \r
356       if (job.jobId != null)\r
357       {\r
358         if (job.jobId.startsWith("Broken"))\r
359         {\r
360           job.result = (vamsas.objects.simple.Result)new JpredResult();\r
361           job.result.setInvalid(true);\r
362           job.result.setStatus("Submission " + job.jobId);\r
363         }\r
364         else\r
365         {\r
366           job.submitted = true;\r
367           job.subjobComplete = false;\r
368           Cache.log.info(WsUrl + " Job Id '" + job.jobId + "'");\r
369         }\r
370       }\r
371       else\r
372       {\r
373         throw new Exception("Server timed out - try again later\n");\r
374       }\r
375     }\r
376     catch (Exception e)\r
377     {\r
378       if (e.getMessage().indexOf("Exception") > -1)\r
379       {\r
380         wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_SERVERERROR);\r
381         wsInfo.setProgressText(j.jobnum,\r
382             "Failed to submit the prediction. (Just close the window)\n"\r
383             +\r
384         "It is most likely that there is a problem with the server.\n");\r
385         System.err.println(\r
386             "JPredWS Client: Failed to submit the prediction. Quite possibly because of a server error - see below)\n" +\r
387             e.getMessage() + "\n");\r
388 \r
389         jalview.bin.Cache.log.warn("Server Exception", e);\r
390       }\r
391       else\r
392       {\r
393         wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR);\r
394         // JBPNote - this could be a popup informing the user of the problem.\r
395         wsInfo.appendProgressText(j.jobnum,\r
396             "Failed to submit the prediction:\n"\r
397             + e.getMessage() +\r
398             wsInfo.getProgressText());\r
399 \r
400         jalview.bin.Cache.log.debug("Failed Submission of job " + j.jobnum, e);\r
401 \r
402       }\r
403       j.allowedServerExceptions = -1;\r
404       j.subjobComplete = true;\r
405     }\r
406   }\r
407 \r
408   void parseResult()\r
409   {\r
410     int results = 0; // number of result sets received\r
411     JobStateSummary finalState = new JobStateSummary();\r
412     try\r
413     {\r
414       for (int j = 0; j < jobs.length; j++)\r
415       {\r
416         finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);\r
417         if (jobs[j].submitted && jobs[j].subjobComplete && jobs[j].hasResults())\r
418         {\r
419           results++;\r
420         }\r
421       }\r
422     }\r
423     catch (Exception ex)\r
424     {\r
425 \r
426       Cache.log.error("Unexpected exception when processing results for " +\r
427           altitle, ex);\r
428       wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);\r
429     }\r
430     if (results > 0)\r
431     {\r
432       wsInfo.showResultsNewFrame\r
433       .addActionListener(new java.awt.event.ActionListener()\r
434       {\r
435         public void actionPerformed(\r
436             java.awt.event.ActionEvent evt)\r
437         {\r
438           displayResults(true);\r
439         }\r
440       });\r
441       wsInfo.mergeResults\r
442       .addActionListener(new java.awt.event.ActionListener()\r
443       {\r
444         public void actionPerformed(\r
445             java.awt.event.ActionEvent evt)\r
446         {\r
447           displayResults(false);\r
448         }\r
449       });\r
450       wsInfo.setResultsReady();\r
451     }\r
452     else\r
453     {\r
454       wsInfo.setFinishedNoResults();\r
455     }\r
456   }\r
457 \r
458   void displayResults(boolean newWindow)\r
459   {\r
460     // TODO: cope with multiple subjobs.\r
461     if (jobs != null)\r
462     {\r
463       Object[] res = null;\r
464       boolean msa=false;\r
465       for (int jn = 0; jn < jobs.length; jn++)\r
466       {\r
467         Object[] jobres = null;\r
468         JPredJob j = (JPredJob) jobs[jn];\r
469 \r
470         if (j.hasResults())\r
471         {\r
472           // hack - we only deal with all single seuqence predictions or all profile predictions\r
473           msa = (j.msa!=null) ? true : msa;\r
474           try\r
475           {\r
476             jalview.bin.Cache.log.debug("Parsing output of job " + jn);\r
477             jobres = j.getResultSet();\r
478             jalview.bin.Cache.log.debug("Finished parsing output.");\r
479             if (jobs.length==1)\r
480               res = jobres;\r
481             else {\r
482               // do merge with other job results\r
483               throw new Error("Multiple JNet subjob merging not yet implemented.");\r
484             }\r
485           }\r
486           catch (Exception e)\r
487           {\r
488             jalview.bin.Cache.log.error(\r
489                 "JNet Client: JPred Annotation Parse Error",\r
490                 e);\r
491             wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR);\r
492             wsInfo.appendProgressText(j.jobnum,\r
493                 OutputHeader + "\n" +\r
494                 j.result.getStatus() +\r
495                 "\nInvalid JNet job result data!\n" +\r
496                 e.getMessage());\r
497             j.result.setBroken(true);\r
498           }\r
499         }\r
500       }\r
501 \r
502       if (res != null)\r
503       {\r
504         if (newWindow)\r
505         {\r
506           AlignFrame af;\r
507           if (input==null) {\r
508             if (res[1]!=null) {\r
509               af = new AlignFrame((Alignment)res[0], (ColumnSelection) res[1], AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);\r
510             } else {\r
511               af = new AlignFrame((Alignment)res[0], AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);\r
512             }\r
513           } else {\r
514             /*java.lang.Object[] alandcolsel = input.getAlignmentAndColumnSelection(alignFrame.getViewport().getGapCharacter());\r
515 \r
516             if (((SequenceI[])alandcolsel[0])[0].getLength()!=res.getWidth()) {\r
517               if (msa) {\r
518                 throw new Error("Implementation Error! ColumnSelection from input alignment will not map to result alignment!");\r
519               }\r
520             }\r
521             if (!msa) {\r
522               // update hidden regions to account for loss of gaps in profile. - if any\r
523               // gapMap returns insert list, interpreted as delete list by pruneDeletions\r
524               //((ColumnSelection) alandcolsel[1]).pruneDeletions(ShiftList.parseMap(((SequenceI[]) alandcolsel[0])[0].gapMap()));\r
525             }*/\r
526 \r
527             af = new AlignFrame((Alignment) res[0], (ColumnSelection) res[1],AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);\r
528           }\r
529           Desktop.addInternalFrame(af, altitle,\r
530               AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);\r
531         }\r
532         else\r
533         {\r
534           Cache.log.info("Append results onto existing alignment.");\r
535         }\r
536       }\r
537     }\r
538   }\r
539   void pollJob(WSJob job)\r
540   throws Exception\r
541   {\r
542     job.result = server.getresult(job.jobId);\r
543   }\r
544   public boolean isCancellable()\r
545   {\r
546     return false;\r
547   }\r
548 \r
549   public void cancelJob()\r
550   {\r
551     throw new Error("Implementation error!");\r
552   }\r
553 \r
554   public boolean canMergeResults()\r
555   {\r
556     return false;\r
557   }\r
558 \r
559 }\r
560 \r
561 \r