multiple subjobs can be cancelled and gui only allows results when a
[jalview.git] / src / jalview / ws / MsaWSThread.java
1 package jalview.ws;
2
3 import java.util.*;
4
5 import javax.swing.*;
6
7 import jalview.analysis.*;
8 import jalview.bin.*;
9 import jalview.datamodel.*;
10 import jalview.datamodel.Alignment;
11 import jalview.gui.*;
12 import vamsas.objects.simple.MsaResult;
13
14 /**
15  * <p>
16  * Title:
17  * </p>
18  *
19  * <p>
20  * Description:
21  * </p>
22  *
23  * <p>
24  * Copyright: Copyright (c) 2004
25  * </p>
26  *
27  * <p>
28  * Company: Dundee University
29  * </p>
30  *
31  * @author not attributable
32  * @version 1.0
33  */
34 class MsaWSThread
35     extends Thread implements WSClientI
36 {
37   jalview.gui.AlignFrame alignFrame;
38
39   WebserviceInfo wsInfo = null;
40
41   String WebServiceName = null;
42
43   String OutputHeader;
44   AlignmentView input;
45   boolean submitGaps = false; // pass sequences including gaps to alignment
46
47   // service
48
49   boolean preserveOrder = true; // and always store and recover sequence
50
51   // order
52
53   class MsaWSJob
54   {
55     int jobnum = 0; // WebServiceInfo pane for this job
56
57     String jobId; // ws job ticket
58
59     vamsas.objects.simple.MsaResult result = null;
60
61     vamsas.objects.simple.SequenceSet seqs = new vamsas.objects.simple.
62         SequenceSet();
63
64     /**
65      * MsaWSJob
66      *
67      * @param jobNum
68      *            int
69      * @param jobId
70      *            String
71      */
72     public MsaWSJob(int jobNum, SequenceI[] inSeqs)
73     {
74       this.jobnum = jobNum;
75       if (!prepareInput(inSeqs, 2))
76       {
77         submitted = true;
78         subjobComplete = true;
79         result = new MsaResult();
80         result.setFinished(true);
81         result.setStatus("Job never ran - input returned to user.");
82       }
83
84     }
85
86     int allowedServerExceptions = 3; // thread dies if too many
87
88     // exceptions.
89     boolean submitted = false;
90     boolean subjobComplete = false;
91
92     Hashtable SeqNames = new Hashtable();
93     Vector emptySeqs = new Vector();
94     /**
95      * prepare input sequences for MsaWS service
96      * @param seqs jalview sequences to be prepared
97      * @param minlen minimum number of residues required for this MsaWS service
98      * @return true if seqs contains sequences to be submitted to service.
99      */
100     private boolean prepareInput(SequenceI[] seqs, int minlen)
101     {
102       int nseqs = 0;
103       if (minlen < 0)
104       {
105         throw new Error("Implementation error: minlen must be zero or more.");
106       }
107       for (int i = 0; i < seqs.length; i++)
108       {
109         if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
110         {
111           nseqs++;
112         }
113       }
114       boolean valid = nseqs > 1; // need at least two seqs
115       vamsas.objects.simple.Sequence[] seqarray =
116           (valid)
117           ? new vamsas.objects.simple.Sequence[nseqs]
118           : null;
119       for (int i = 0, n = 0; i < seqs.length; i++)
120       {
121
122         String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same
123         // for
124         // any
125         // subjob
126         SeqNames.put(newname, jalview.analysis.SeqsetUtils
127                      .SeqCharacterHash(seqs[i]));
128         if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
129         {
130           seqarray[n] = new vamsas.objects.simple.Sequence();
131           seqarray[n].setId(newname);
132           seqarray[n++].setSeq( (submitGaps) ? seqs[i].getSequence()
133                                : AlignSeq.extractGaps(
134                                    jalview.util.Comparison.GapChars, seqs[i]
135                                    .getSequence()));
136         }
137         else
138         {
139           String empty = null;
140           if (seqs[i].getEnd() >= seqs[i].getStart())
141           {
142             empty = (submitGaps) ? seqs[i].getSequence()
143                 : AlignSeq.extractGaps(
144                     jalview.util.Comparison.GapChars, seqs[i]
145                     .getSequence());
146           }
147           emptySeqs.add(new String[]
148                         {newname, empty});
149         }
150       }
151       this.seqs = new vamsas.objects.simple.SequenceSet();
152       this.seqs.setSeqs(seqarray);
153       return valid;
154     }
155
156     /**
157      *
158      * @return true if getAlignment will return a valid alignment result.
159      */
160     public boolean hasResults()
161     {
162       if (subjobComplete && result != null && result.isFinished()
163           && result.getMsa() != null && result.getMsa().getSeqs() != null)
164       {
165         return true;
166       }
167       return false;
168     }
169
170     public Object[] getAlignment()
171     {
172
173       if (result != null && result.isFinished())
174       {
175         SequenceI[] alseqs = null;
176         char alseq_gapchar = '-';
177         int alseq_l = 0;
178         if (result.getMsa() != null)
179         {
180           alseqs = getVamsasAlignment(result.getMsa());
181           alseq_gapchar = result.getMsa().getGapchar().charAt(0);
182           alseq_l = alseqs.length;
183         }
184         if (emptySeqs.size() > 0)
185         {
186           SequenceI[] t_alseqs = new SequenceI[alseq_l + emptySeqs.size()];
187           // get width
188           int i, w = 0;
189           if (alseq_l > 0)
190           {
191             for (i = 0, w = alseqs[0].getLength(); i < alseq_l; i++)
192             {
193               if (w < alseqs[i].getLength())
194               {
195                 w = alseqs[i].getLength();
196               }
197               t_alseqs[i] = alseqs[i];
198               alseqs[i] = null;
199             }
200           }
201           // check that aligned width is at least as wide as emptySeqs width.
202           int ow = w, nw = w;
203           for (i = 0, w = emptySeqs.size(); i < w; i++)
204           {
205             String[] es = (String[]) emptySeqs.get(i);
206             if (es != null && es[1] != null)
207             {
208               int sw = es[1].length();
209               if (nw < sw)
210               {
211                 nw = sw;
212               }
213             }
214           }
215           // make a gapped string.
216           StringBuffer insbuff = new StringBuffer(w);
217           for (i = 0; i < nw; i++)
218           {
219             insbuff.append(alseq_gapchar);
220           }
221           if (ow < nw)
222           {
223             for (i = 0; i < alseq_l; i++)
224             {
225               int sw = t_alseqs[i].getLength();
226               if (nw > sw)
227               {
228                 // pad at end
229                 alseqs[i].setSequence(t_alseqs[i].getSequence() +
230                                       insbuff.substring(0, sw - nw));
231               }
232             }
233           }
234           for (i = 0, w = emptySeqs.size(); i < w; i++)
235           {
236             String[] es = (String[]) emptySeqs.get(i);
237             if (es[1] == null)
238             {
239               t_alseqs[i +
240                   alseq_l] = new jalview.datamodel.Sequence(es[0],
241                   insbuff.toString(), 1, 0);
242             }
243             else
244             {
245               if (es[1].length() < nw)
246               {
247                 t_alseqs[i +
248                     alseq_l] = new jalview.datamodel.Sequence(es[0],
249                     es[1] + insbuff.substring(0, nw - es[1].length()), 1,
250                     1 + es[1].length());
251               }
252               else
253               {
254                 t_alseqs[i +
255                     alseq_l] = new jalview.datamodel.Sequence(es[0], es[1]);
256               }
257             }
258           }
259           alseqs = t_alseqs;
260         }
261         AlignmentOrder msaorder = new AlignmentOrder(alseqs);
262         // always recover the order - makes parseResult()'s life easier.
263         jalview.analysis.AlignmentSorter.recoverOrder(alseqs);
264         // account for any missing sequences
265         jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs);
266         return new Object[]
267             {
268             alseqs, msaorder};
269       }
270       return null;
271     }
272
273     boolean cancelled = false;
274     /**
275      * mark subjob as cancelled and set result object appropriatly
276      */
277     void cancel() {
278       cancelled=true;
279       subjobComplete = true;
280       result = null;
281     }
282   }
283
284   MsaWSJob jobs[] = null;
285
286   String alTitle; // name which will be used to form new alignment window.
287
288   boolean jobComplete = false;
289
290   Alignment dataset; // dataset to which the new alignment will be
291
292   // associated.
293
294   ext.vamsas.MuscleWS server = null;
295
296   String WsUrl = null;
297
298   /**
299    * set basic options for this (group) of Msa jobs
300    *
301    * @param subgaps
302    *            boolean
303    * @param presorder
304    *            boolean
305    */
306   MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
307               WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
308               AlignmentView alview,
309               String wsname, boolean subgaps, boolean presorder)
310   {
311     this.server = server;
312     this.WsUrl = wsUrl;
313     this.wsInfo = wsinfo;
314     this.WebServiceName = wsname;
315     this.input = alview;
316     this.submitGaps = subgaps;
317     this.preserveOrder = presorder;
318     this.alignFrame = alFrame;
319   }
320
321   /**
322    * create one or more Msa jobs to align visible seuqences in _msa
323    *
324    * @param title
325    *            String
326    * @param _msa
327    *            AlignmentView
328    * @param subgaps
329    *            boolean
330    * @param presorder
331    *            boolean
332    * @param seqset
333    *            Alignment
334    */
335   MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
336               WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
337               String wsname, String title, AlignmentView _msa, boolean subgaps,
338               boolean presorder, Alignment seqset)
339   {
340     this(server, wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder);
341     OutputHeader = wsInfo.getProgressText();
342     alTitle = title;
343     dataset = seqset;
344     SeqCigar[] msa = _msa.getSequences();
345     int[] contigs = _msa.getContigs();
346     int njobs = 1;
347     if (contigs != null && contigs.length > 0)
348     {
349       int start = 0;
350       njobs = 0;
351       int width = _msa.getWidth();
352       for (int contig = 0; contig < contigs.length; contig += 3)
353       {
354         if ( (contigs[contig + 1] - start) > 0)
355         {
356           njobs++;
357         }
358         width += contigs[contig + 2]; // end up with full region width (including hidden regions)
359         start = contigs[contig + 1] + contigs[contig + 2];
360       }
361       if (start < width)
362       {
363         njobs++;
364       }
365       jobs = new MsaWSJob[njobs];
366       start = 0;
367       int j = 0;
368       for (int contig = 0; contig < contigs.length; contig += 3)
369       {
370         if (contigs[contig + 1] - start > 0)
371         {
372           SequenceI mseq[] = new SequenceI[msa.length];
373           for (int s = 0; s < mseq.length; s++)
374           {
375             mseq[s] = msa[s].getSeq('-').getSubSequence(start,
376                 contigs[contig + 1]);
377           }
378           if (j != 0)
379           {
380             jobs[j] = new MsaWSJob(wsinfo.addJobPane(), mseq);
381           }
382           else
383           {
384             jobs[j] = new MsaWSJob(0, mseq);
385           }
386           wsinfo.setProgressName("region " + jobs[j].jobnum, jobs[j].jobnum);
387           wsinfo.setProgressText(jobs[j].jobnum, OutputHeader);
388           j++;
389         }
390         start = contigs[contig + 1] + contigs[contig + 2];
391       }
392       if (start < width)
393       {
394         SequenceI mseq[] = new SequenceI[msa.length];
395         for (int s = 0; s < mseq.length; s++)
396         {
397           mseq[s] = msa[s].getSeq('-').getSubSequence(start,
398               width + 1);
399         }
400         if (j != 0)
401         {
402           jobs[j] = new MsaWSJob(wsinfo.addJobPane(), mseq);
403         }
404         else
405         {
406           jobs[j] = new MsaWSJob(0, mseq);
407         }
408         wsinfo.setProgressName("region " + jobs[j].jobnum, jobs[j].jobnum);
409         wsinfo.setProgressText(jobs[j].jobnum, OutputHeader);
410         j++;
411       }
412     }
413     else
414     {
415       SequenceI mseq[] = new SequenceI[msa.length];
416       for (int s = 0; s < mseq.length; s++)
417       {
418         mseq[s] = msa[s].getSeq('-');
419       }
420       jobs = new MsaWSJob[1];
421       wsinfo.setProgressText(OutputHeader); // ensures default text
422       jobs[0] = new MsaWSJob(0, mseq);
423     }
424   }
425
426   public boolean isCancellable()
427   {
428     return true;
429   }
430
431   public void cancelJob()
432   {
433     if (!jobComplete && jobs != null)
434     {
435       boolean cancelled = true;
436       for (int job = 0; job < jobs.length; job++)
437       {
438         if (jobs[job].submitted && !jobs[job].subjobComplete)
439         {
440           String cancelledMessage = "";
441           try
442           {
443             vamsas.objects.simple.WsJobId cancelledJob = server
444                 .cancel(jobs[job].jobId);
445             if (cancelledJob.getStatus() == 2)
446             {
447               // CANCELLED_JOB
448               cancelledMessage = "Job cancelled.";
449               jobs[job].cancel();
450               wsInfo.setStatus(jobs[job].jobnum,
451                                WebserviceInfo.STATE_CANCELLED_OK);
452             }
453             else if (cancelledJob.getStatus() == 3)
454             {
455               // VALID UNSTOPPABLE JOB
456               cancelledMessage +=
457                   "Server cannot cancel this job. just close the window.\n";
458               cancelled = false;
459               // wsInfo.setStatus(jobs[job].jobnum,
460               //                 WebserviceInfo.STATE_RUNNING);
461             }
462
463             if (cancelledJob.getJobId() != null)
464             {
465               cancelledMessage += ("[" + cancelledJob.getJobId() + "]");
466             }
467
468             cancelledMessage += "\n";
469           }
470           catch (Exception exc)
471           {
472             cancelledMessage +=
473                 ("\nProblems cancelling the job : Exception received...\n"
474                  + exc + "\n");
475             Cache.log.warn("Exception whilst cancelling "+jobs[job].jobId,exc);
476           }
477           wsInfo.setProgressText(jobs[job].jobnum, OutputHeader
478                                  + cancelledMessage + "\n");
479         }
480       }
481       if (cancelled)
482       {
483         wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
484         jobComplete = true;
485       }
486       this.interrupt(); // kick thread to update job states.
487     }
488     else
489     {
490       if (!jobComplete)
491       {
492         wsInfo
493             .setProgressText(OutputHeader
494                              + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
495       }
496     }
497   }
498
499   class JobStateSummary {
500     int running = 0;
501     int queuing = 0;
502     int finished = 0;
503     int error = 0;
504     int serror = 0;
505     int cancelled=0;
506     int results=0;
507     void updateJobPanelState(WebserviceInfo wsInfo, MsaWSJob j) {
508       if (j.result != null)
509       {
510         String progheader = "";
511         // Parse state of job[j]
512         if (j.result.isRunning())
513         {
514           running++;
515           wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_RUNNING);
516         }
517         else if (j.result.isQueued())
518         {
519           queuing++;
520           wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_QUEUING);
521         }
522         else if (j.result.isFinished())
523         {
524           finished++;
525           j.subjobComplete = true;
526           if (j.hasResults())
527             results++;
528           wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_OK);
529         }
530         else if (j.result.isFailed())
531         {
532           progheader += "Job failed.\n";
533           j.subjobComplete = true;
534           wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR);
535           error++;
536         }
537         else if (j.result.isServerError())
538         {
539           serror++;
540           j.subjobComplete = true;
541           wsInfo.setStatus(j.jobnum,
542                            WebserviceInfo.STATE_STOPPED_SERVERERROR);
543         }
544         else if (j.result.isBroken() || j.result.isFailed())
545         {
546           error++;
547           j.subjobComplete = true;
548           wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR);
549         }
550         // and pass on any sub-job messages to the user
551         wsInfo.setProgressText(j.jobnum, OutputHeader);
552         wsInfo.appendProgressText(j.jobnum, progheader);
553         if (j.result.getStatus() != null)
554         {
555           wsInfo.appendProgressText(j.jobnum, j.result.getStatus());
556         }
557       }
558       else
559       {
560         if (j.submitted && j.subjobComplete)
561         {
562           if (j.allowedServerExceptions == 0)
563           {
564             serror++;
565           }
566           else if (j.result == null)
567           {
568             error++;
569           }
570         }
571       }
572     }
573   }
574   public void run()
575   {
576     JobStateSummary jstate=null;
577     while (!jobComplete)
578     {
579       jstate = new JobStateSummary();
580       for (int j = 0; j < jobs.length; j++)
581       {
582
583         if (!jobs[j].submitted && jobs[j].seqs.getSeqs() != null)
584         {
585           StartJob(jobs[j]);
586         }
587
588         if (jobs[j].submitted && !jobs[j].subjobComplete)
589         {
590           try
591           {
592             if ( (jobs[j].result = server.getResult(jobs[j].jobId)) == null)
593             {
594               throw (new Exception(
595                   "Timed out when communicating with server\nTry again later.\n"));
596             }
597             jalview.bin.Cache.log.debug("Job " + j + " Result state " +
598                                         jobs[j].result.getState()
599                                         + "(ServerError=" +
600                                         jobs[j].result.isServerError() + ")");
601           }
602           catch (Exception ex)
603           {
604             // Deal with Transaction exceptions
605             wsInfo.appendProgressText(jobs[j].jobnum, "\n" + WebServiceName
606                                       + " Server exception!\n" + ex.getMessage());
607             Cache.log.warn(WebServiceName + " job(" + jobs[j].jobnum
608                            + ") Server exception: " + ex.getMessage());
609
610             if (jobs[j].allowedServerExceptions > 0)
611             {
612               jobs[j].allowedServerExceptions--;
613               Cache.log.debug("Sleeping after a server exception.");
614               try
615               {
616                 Thread.sleep(5000);
617               }
618               catch (InterruptedException ex1)
619               {
620               }
621             }
622             else
623             {
624               Cache.log.warn("Dropping job " + j + " " + jobs[j].jobId);
625               jobs[j].subjobComplete = true;
626               wsInfo.setStatus(jobs[j].jobnum,
627                                WebserviceInfo.STATE_STOPPED_SERVERERROR);
628             }
629           }
630           catch (OutOfMemoryError er)
631           {
632             jobComplete = true;
633             jobs[j].subjobComplete = true;
634             jobs[j].result = null; // may contain out of date result object
635             wsInfo.setStatus(jobs[j].jobnum,
636                              WebserviceInfo.STATE_STOPPED_ERROR);
637             JOptionPane
638                 .showInternalMessageDialog(
639                     Desktop.desktop,
640                     "Out of memory handling result for job !!"
641                     +
642                 "\nSee help files for increasing Java Virtual Machine memory.",
643                     "Out of memory", JOptionPane.WARNING_MESSAGE);
644             Cache.log.error("Out of memory when retrieving Job " + j + " id:" +
645                             WsUrl + "/" + jobs[j].jobId, er);
646             System.gc();
647           }
648         }
649         jstate.updateJobPanelState(wsInfo, jobs[j]);
650       }
651       // Decide on overall state based on collected jobs[] states
652       if (jstate.running > 0)
653       {
654         wsInfo.setStatus(WebserviceInfo.STATE_RUNNING);
655       }
656       else if (jstate.queuing > 0)
657       {
658         wsInfo.setStatus(WebserviceInfo.STATE_QUEUING);
659       }
660       else
661       {
662         jobComplete = true;
663         if (jstate.finished > 0)
664         {
665           wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_OK);
666         }
667         else if (jstate.error > 0)
668         {
669           wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
670         }
671         else if (jstate.serror > 0)
672         {
673           wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
674         }
675       }
676       if (!jobComplete)
677       {
678         try
679         {
680           Thread.sleep(5000);
681         }
682         catch (InterruptedException e)
683         {
684           Cache.log.debug("Interrupted sleep waiting for next job poll.", e);
685         }
686         // System.out.println("I'm alive "+alTitle);
687       }
688     }
689     if (jobComplete)
690     {
691       parseResult(); // tidy up and make results available to user
692     }
693   }
694
695   void StartJob(MsaWSJob j)
696   {
697     if (j.submitted)
698     {
699       if (Cache.log.isDebugEnabled())
700       {
701         Cache.log.debug("Tried to submit an already submitted job " + j.jobId);
702       }
703       return;
704     }
705     if (j.seqs.getSeqs() == null)
706     {
707       // special case - selection consisted entirely of empty sequences...
708       j.submitted = true;
709       j.result = new MsaResult();
710       j.result.setFinished(true);
711       j.result.setStatus("Empty Alignment Job");
712       j.result.setMsa(null);
713     }
714     try
715     {
716       vamsas.objects.simple.WsJobId jobsubmit = server.align(j.seqs);
717
718       if ( (jobsubmit != null) && (jobsubmit.getStatus() == 1))
719       {
720         j.jobId = jobsubmit.getJobId();
721         j.submitted = true;
722         j.subjobComplete = false;
723         // System.out.println(WsURL + " Job Id '" + jobId + "'");
724       }
725       else
726       {
727         if (jobsubmit == null)
728         {
729           throw new Exception(
730               "Server at "
731               + WsUrl
732               +
733               " returned null object, it probably cannot be contacted. Try again later ?");
734         }
735
736         throw new Exception(jobsubmit.getJobId());
737       }
738     }
739     catch (Exception e)
740     {
741       // TODO: JBPNote catch timeout or other fault types explicitly
742       // For unexpected errors
743       System.err
744           .println(WebServiceName
745                    + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
746                    + "When contacting Server:" + WsUrl + "\n"
747                    + e.toString() + "\n");
748       j.allowedServerExceptions = 0;
749       wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
750       wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_SERVERERROR);
751       wsInfo
752           .appendProgressText(
753               j.jobnum,
754               "Failed to submit sequences for alignment.\n"
755               + "It is most likely that there is a problem with the server.\n"
756               + "Just close the window\n");
757
758       // e.printStackTrace(); // TODO: JBPNote DEBUG
759     }
760   }
761
762   private jalview.datamodel.Sequence[] getVamsasAlignment(
763       vamsas.objects.simple.Alignment valign)
764   {
765     vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs();
766     jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs.
767         length];
768
769     for (int i = 0, j = seqs.length; i < j; i++)
770     {
771       msa[i] = new jalview.datamodel.Sequence(seqs[i].getId(), seqs[i]
772                                               .getSeq());
773     }
774
775     return msa;
776   }
777
778   void parseResult()
779   {
780     int results = 0; // number of result sets received
781     JobStateSummary finalState = new JobStateSummary();
782     try
783     {
784       for (int j = 0; j < jobs.length; j++)
785       {
786         finalState.updateJobPanelState(wsInfo, jobs[j]);
787         if (jobs[j].submitted && jobs[j].subjobComplete && jobs[j].hasResults())
788         {
789           results++;
790           vamsas.objects.simple.Alignment valign = jobs[j].result.getMsa();
791           if (valign != null)
792           {
793             wsInfo.appendProgressText(jobs[j].jobnum,
794                                       "\nAlignment Object Method Notes\n");
795             String[] lines = valign.getMethod();
796             for (int line = 0; line < lines.length; line++)
797             {
798               wsInfo.appendProgressText(jobs[j].jobnum, lines[line] + "\n");
799             }
800             // JBPNote The returned files from a webservice could be
801             //  hidden behind icons in the monitor window that,
802             // when clicked, pop up their corresponding data
803           }
804         }
805       }
806     }
807     catch (Exception ex)
808     {
809
810       Cache.log.error("Unexpected exception when processing results for " +
811                       alTitle, ex);
812       wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
813     }
814     if (results > 0)
815     {
816       wsInfo.showResultsNewFrame
817           .addActionListener(new java.awt.event.ActionListener()
818       {
819         public void actionPerformed(
820             java.awt.event.ActionEvent evt)
821         {
822           displayResults(true);
823         }
824       });
825       wsInfo.mergeResults
826           .addActionListener(new java.awt.event.ActionListener()
827       {
828         public void actionPerformed(
829             java.awt.event.ActionEvent evt)
830         {
831           displayResults(false);
832         }
833       });
834       wsInfo.setResultsReady();
835     }
836   }
837
838   void displayResults(boolean newFrame)
839   {
840     // view input or result data for each block
841     // warn user if a block is input rather than aligned data ?
842
843     int contigs[] = input.getContigs();
844     SeqCigar[] seqs = input.getSequences();
845     SequenceI[] alignment = new SequenceI[seqs.length];
846     ColumnSelection columnselection = new ColumnSelection();
847     Vector alorders = new Vector();
848     if (contigs != null && contigs.length > 0)
849     {
850       int start = 0;
851       int nwidth = 0;
852       int owidth = input.getWidth();
853       int j = 0;
854       for (int contig = 0; contig < contigs.length; contig += 3)
855       {
856         owidth += contigs[contig + 2]; // recover final column width
857         if (contigs[contig + 1] - start > 0)
858         {
859           int width = 0; // subalignment width
860           if (jobs[j].hasResults())
861           {
862             Object[] subalg = jobs[j++].getAlignment();
863             alorders.add(subalg[1]);
864             SequenceI mseq[] = (SequenceI[]) subalg[0];
865             width = mseq[0].getLength();
866             for (int s = 0; s < mseq.length; s++)
867             {
868               if (alignment[s] == null)
869               {
870                 alignment[s] = mseq[s];
871               }
872               else
873               {
874                 alignment[s].setSequence(alignment[s].getSequence() +
875                                          mseq[s].getSequence());
876                 if (mseq[s].getStart() <= mseq[s].getEnd())
877                 {
878                   alignment[s].setEnd(mseq[s].getEnd());
879                 }
880                 ( (AlignmentOrder) subalg[1]).updateSequence(mseq[s],
881                     alignment[s]);
882               }
883             }
884           }
885           else
886           {
887             // recover input data or place gaps
888             if (true)
889             {
890               // recover input data
891               for (int s = 0; s < seqs.length; s++)
892               {
893                 SequenceI oseq = seqs[s].getSeq('-').getSubSequence(start,
894                     contigs[contig + 1]);
895                 if (width < oseq.getLength())
896                 {
897                   width = oseq.getLength();
898                 }
899                 if (alignment[s] == null)
900                 {
901                   alignment[s] = oseq;
902                 }
903                 else
904                 {
905                   alignment[s].setSequence(alignment[s].getSequence() +
906                                            oseq.getSequence());
907                   if (oseq.getEnd() >= oseq.getStart())
908                   {
909                     alignment[s].setEnd(oseq.getEnd());
910                   }
911                 }
912               }
913
914             }
915             j++;
916           }
917           nwidth += width;
918         }
919         // advance to begining of visible region
920         start = contigs[contig + 1] + contigs[contig + 2];
921         // add hidden segment to right of next region
922         for (int s = 0; s < seqs.length; s++)
923         {
924           SequenceI hseq = seqs[s].getSeq('-').getSubSequence(contigs[contig +
925               1], start);
926           if (alignment[s] == null)
927           {
928             alignment[s] = hseq;
929           }
930           else
931           {
932             alignment[s].setSequence(alignment[s].getSequence() +
933                                      hseq.getSequence());
934             if (hseq.getEnd() >= hseq.getStart())
935             {
936               alignment[s].setEnd(hseq.getEnd());
937             }
938           }
939         }
940         // mark hidden segment as hidden in the new alignment
941         columnselection.hideColumns(nwidth, nwidth + contigs[contig + 2] - 1);
942         nwidth += contigs[contig + 2];
943       }
944       // Do final job - if it exists
945       if (j < jobs.length)
946       {
947         int width = 0;
948         if (jobs[j].hasResults())
949         {
950           Object[] subalg = jobs[j].getAlignment();
951           alorders.add(subalg[1]);
952           SequenceI mseq[] = (SequenceI[]) subalg[0];
953           width = mseq[0].getLength();
954           for (int s = 0; s < mseq.length; s++)
955           {
956             if (alignment[s] == null)
957             {
958               alignment[s] = mseq[s];
959             }
960             else
961             {
962               alignment[s].setSequence(alignment[s].getSequence() +
963                                        mseq[s].getSequence());
964               if (mseq[s].getEnd() >= mseq[s].getStart())
965               {
966                 alignment[s].setEnd(mseq[s].getEnd());
967               }
968               ( (AlignmentOrder) subalg[1]).updateSequence(mseq[s], alignment[s]);
969             }
970           }
971         }
972         else
973         {
974           if (start < owidth)
975           {
976             // recover input data or place gaps
977             if (true)
978             {
979               // recover input data
980               for (int s = 0; s < seqs.length; s++)
981               {
982                 SequenceI oseq = seqs[s].getSeq('-').getSubSequence(start,
983                     owidth + 1);
984                 if (width < oseq.getLength())
985                 {
986                   width = oseq.getLength();
987                 }
988                 if (alignment[s] == null)
989                 {
990                   alignment[s] = oseq;
991                 }
992                 else
993                 {
994                   alignment[s].setSequence(alignment[s].getSequence() +
995                                            oseq.getSequence());
996                   if (oseq.getEnd() >= oseq.getStart())
997                   {
998                     alignment[s].setEnd(oseq.getEnd());
999                   }
1000                 }
1001               }
1002               nwidth += width;
1003             }
1004             else
1005             {
1006               // place gaps.
1007               throw new Error("Padding not yet implemented.");
1008             }
1009           }
1010         }
1011       }
1012     }
1013     else
1014     {
1015       if (jobs[0].hasResults())
1016       {
1017         Object[] alg = jobs[0].getAlignment();
1018         alignment = (SequenceI[]) alg[0];
1019         alorders.add(alg[1]);
1020       }
1021       else
1022       {
1023         alignment = SeqCigar.createAlignmentSequences(seqs, '-',
1024             columnselection, null);
1025       }
1026     }
1027     Alignment al = new Alignment(alignment);
1028     if (dataset != null)
1029     {
1030       al.setDataset(dataset);
1031     }
1032
1033     if (newFrame)
1034     {
1035       // TODO: JBPNote Should also rename the query sequence
1036       // sometime...
1037       AlignFrame af = new AlignFrame(al, columnselection);
1038
1039       // >>>This is a fix for the moment, until a better solution is
1040       // found!!<<<
1041       af.getFeatureRenderer().transferSettings(
1042           alignFrame.getFeatureRenderer());
1043       if (alorders.size() > 0)
1044       {
1045         if (alorders.size() == 1)
1046         {
1047           af.addSortByOrderMenuItem(WebServiceName + " Ordering",
1048                                     (AlignmentOrder) alorders.get(0));
1049         }
1050         else
1051         {
1052           // construct a non-redundant ordering set
1053           Vector names = new Vector();
1054           for (int i = 0, l = alorders.size(); i < l; i++)
1055           {
1056             String orderName = new String("Region " + i);
1057             int j = i + 1;
1058             int r = l;
1059             while (j < l)
1060             {
1061               if ( ( (AlignmentOrder) alorders.get(i)).equals( ( (
1062                   AlignmentOrder) alorders.get(j))))
1063               {
1064                 alorders.remove(j);
1065                 l--;
1066                 orderName += "," + j;
1067               }
1068               else
1069               {
1070                 j++;
1071               }
1072             }
1073
1074             if (i == 0 && j == 1)
1075             {
1076               names.add(new String(""));
1077             }
1078             else
1079             {
1080               names.add(orderName);
1081             }
1082           }
1083           for (int i = 0, l = alorders.size(); i < l; i++)
1084           {
1085             af.addSortByOrderMenuItem(WebServiceName + ( (String) names.get(i)) +
1086                                       " Ordering",
1087                                       (AlignmentOrder) alorders.get(i));
1088           }
1089         }
1090       }
1091
1092       Desktop.addInternalFrame(af, alTitle,
1093                                AlignFrame.NEW_WINDOW_WIDTH,
1094                                AlignFrame.NEW_WINDOW_HEIGHT);
1095
1096     }
1097     else
1098     {
1099       System.out.println("MERGE WITH OLD FRAME");
1100
1101     }
1102   }
1103 }