fixing residue clip bug in MsaWSThread and adding minimum sequence length constraints.
[jalview.git] / src / jalview / ws / MsaWSThread.java
1 package jalview.ws;
2
3 import jalview.datamodel.AlignmentView;
4 import jalview.datamodel.AlignmentOrder;
5 import jalview.datamodel.ColumnSelection;
6 import jalview.gui.WebserviceInfo;
7 import jalview.analysis.AlignSeq;
8 import jalview.bin.Cache;
9 import jalview.gui.AlignFrame;
10 import javax.swing.JOptionPane;
11
12 import vamsas.objects.simple.MsaResult;
13 import vamsas.objects.simple.Result;
14 import jalview.datamodel.Alignment;
15 import jalview.datamodel.SeqCigar;
16 import jalview.gui.Desktop;
17 import jalview.datamodel.SequenceI;
18 import java.util.Hashtable;
19 import java.util.Vector;
20
21 /**
22  * <p>
23  * Title:
24  * </p>
25  * 
26  * <p>
27  * Description:
28  * </p>
29  * 
30  * <p>
31  * Copyright: Copyright (c) 2004
32  * </p>
33  * 
34  * <p>
35  * Company: Dundee University
36  * </p>
37  * 
38  * @author not attributable
39  * @version 1.0
40  */
41 class MsaWSThread extends Thread implements WSClientI {
42   jalview.gui.AlignFrame alignFrame;
43   
44   WebserviceInfo wsInfo = null;
45   
46   String WebServiceName = null;
47   
48   String OutputHeader;
49   AlignmentView input;
50   boolean submitGaps = false; // pass sequences including gaps to alignment
51   
52   // service
53   
54   boolean preserveOrder = true; // and always store and recover sequence
55   
56   // order
57   
58   class MsaWSJob {
59     int jobnum = 0; // WebServiceInfo pane for this job
60     
61     String jobId; // ws job ticket
62     
63     vamsas.objects.simple.MsaResult result = null;
64     
65     vamsas.objects.simple.SequenceSet seqs = new vamsas.objects.simple.SequenceSet();
66     
67     /**
68      * MsaWSJob
69      * 
70      * @param jobNum
71      *            int
72      * @param jobId
73      *            String
74      */
75     public MsaWSJob(int jobNum, SequenceI[] inSeqs) {
76       this.jobnum = jobNum;
77       if (!prepareInput(inSeqs,2)) {
78         submitted=true;
79         subjobComplete=true;
80         result = new MsaResult();
81         result.setFinished(true);
82         result.setStatus("Job never ran - input returned to user.");
83       }
84         
85     }
86     
87     int allowedServerExceptions = 3; // thread dies if too many
88     
89     // exceptions.
90     boolean submitted=false;
91     boolean subjobComplete = false;
92     
93     Hashtable SeqNames = new Hashtable();
94     Vector emptySeqs = new Vector();
95     /**
96      * prepare input sequences for MsaWS service
97      * @param seqs jalview sequences to be prepared
98      * @param minlen minimum number of residues required for this MsaWS service
99      * @return true if seqs contains sequences to be submitted to service.
100      */
101     private boolean prepareInput(SequenceI[] seqs, int minlen) {
102       int nseqs = 0;
103       if (minlen<0)
104         throw new Error("Implementation error: minlen must be zero or more.");
105       for (int i = 0; i < seqs.length; i++) {
106         if (seqs[i].getEnd()-seqs[i].getStart()>minlen-1) {
107           nseqs++;
108         }
109       }
110       boolean valid=nseqs>1; // need at least two seqs
111       vamsas.objects.simple.Sequence[] seqarray = 
112           (valid) 
113         ? new vamsas.objects.simple.Sequence[nseqs]
114                                              :null;
115         for (int i = 0, n = 0; i < seqs.length; i++) {
116           
117           String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same
118           // for
119           // any
120           // subjob
121           SeqNames.put(newname, jalview.analysis.SeqsetUtils
122               .SeqCharacterHash(seqs[i]));
123           if (valid && seqs[i].getEnd()-seqs[i].getStart()>minlen-1) {
124             seqarray[n] = new vamsas.objects.simple.Sequence();
125             seqarray[n].setId(newname);
126             seqarray[n++].setSeq((submitGaps) ? seqs[i].getSequence()
127                 : AlignSeq.extractGaps(
128                     jalview.util.Comparison.GapChars, seqs[i]
129                                                            .getSequence()));
130           } else {
131             String empty=null;
132             if (seqs[i].getEnd()>=seqs[i].getStart()) {
133               empty = (submitGaps) ? seqs[i].getSequence()
134                     : AlignSeq.extractGaps(
135                         jalview.util.Comparison.GapChars, seqs[i]
136                                                                .getSequence());              
137             }
138             emptySeqs.add(new String[] { newname, empty});
139           }
140         }
141         this.seqs = new vamsas.objects.simple.SequenceSet();
142         this.seqs.setSeqs(seqarray);
143         return valid;
144     }
145     /**
146      * 
147      * @return true if getAlignment will return a valid alignment result.
148      */
149     public boolean hasResults() {
150       if (subjobComplete && result!=null && jobs[0].result.isFinished())
151         return true;
152       return false;
153     }
154     public Object[] getAlignment() {
155       
156       if (result!=null && result.isFinished()) {
157         SequenceI[] alseqs=null;
158         char alseq_gapchar='-';
159         int alseq_l=0;
160         if (result.getMsa() != null) {
161           alseqs = getVamsasAlignment(result.getMsa());
162           alseq_gapchar=result.getMsa().getGapchar().charAt(0);
163           alseq_l = alseqs.length;
164         }
165         if (emptySeqs.size()>0) {
166           SequenceI[] t_alseqs = new SequenceI[alseq_l+emptySeqs.size()];
167           // get width
168           int i,w=0;
169           if (alseq_l>0) {
170             for (i=0,w=alseqs[0].getLength(); i<alseq_l; i++) {
171               if (w<alseqs[i].getLength())
172                 w=alseqs[i].getLength();
173               t_alseqs[i] = alseqs[i];
174               alseqs[i] = null;
175             }
176           }
177           // check that aligned width is at least as wide as emptySeqs width.
178           int ow=w,nw=w;
179           for (i=0, w=emptySeqs.size(); i<w; i++) {
180             String[] es=(String[]) emptySeqs.get(i);
181             if (es!=null && es[1]!=null) {
182               int sw=es[1].length();
183               if (nw<sw)
184                 nw=sw;
185             }
186           }
187           // make a gapped string.
188           StringBuffer insbuff=new StringBuffer(w);
189           for (i=0; i<nw; i++)
190             insbuff.append(alseq_gapchar);
191           if (ow<nw) {
192             for (i=0; i<alseq_l; i++) {
193               int sw=t_alseqs[i].getLength();
194               if (nw>sw) {
195                 // pad at end
196                 alseqs[i].setSequence(t_alseqs[i].getSequence()+insbuff.substring(0,sw-nw));
197               }
198             }
199           }
200           for (i=0, w=emptySeqs.size(); i<w; i++) {
201             String[] es=(String[]) emptySeqs.get(i);
202             if (es[1]==null)
203               t_alseqs[i+alseq_l] = new jalview.datamodel.Sequence(es[0], insbuff.toString(),1,0);
204             else {
205               if (es[1].length()<nw) {
206                 t_alseqs[i+alseq_l] = new jalview.datamodel.Sequence(es[0], es[1]+insbuff.substring(0, nw-es[1].length()),1,1+es[1].length());
207               } else {
208                 t_alseqs[i+alseq_l] = new jalview.datamodel.Sequence(es[0], es[1]);
209               }
210             }
211           }
212           alseqs = t_alseqs;
213         } 
214         AlignmentOrder msaorder = new AlignmentOrder(alseqs);
215         // always recover the order - makes parseResult()'s life easier.
216         jalview.analysis.AlignmentSorter.recoverOrder(alseqs);
217         // account for any missing sequences
218         jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs);
219         return new Object[] { alseqs, msaorder};
220       }
221       return null;
222     }
223   }
224   MsaWSJob jobs[] = null;
225   
226   String alTitle; // name which will be used to form new alignment window.
227   
228   boolean jobComplete = false;
229   
230   Alignment dataset; // dataset to which the new alignment will be
231   
232   // associated.
233   
234   ext.vamsas.MuscleWS server = null;
235   
236   String WsUrl = null;
237   
238   /**
239    * set basic options for this (group) of Msa jobs
240    * 
241    * @param subgaps
242    *            boolean
243    * @param presorder
244    *            boolean
245    */
246   MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
247       WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame, AlignmentView alview,
248       String wsname, boolean subgaps, boolean presorder) {
249     this.server = server;
250     this.WsUrl = wsUrl;
251     this.wsInfo = wsinfo;
252     this.WebServiceName = wsname;
253     this.input = alview;
254     this.submitGaps = subgaps;
255     this.preserveOrder = presorder;
256     this.alignFrame = alFrame;
257   }
258   
259   /**
260    * create one or more Msa jobs to align visible seuqences in _msa
261    * 
262    * @param title
263    *            String
264    * @param _msa
265    *            AlignmentView
266    * @param subgaps
267    *            boolean
268    * @param presorder
269    *            boolean
270    * @param seqset
271    *            Alignment
272    */
273   MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
274       WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
275       String wsname, String title, AlignmentView _msa, boolean subgaps,
276       boolean presorder, Alignment seqset) {
277     this(server, wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder);
278     OutputHeader = wsInfo.getProgressText();
279     alTitle = title;
280     dataset = seqset;
281     SeqCigar[] msa = _msa.getSequences();
282     int[] contigs = _msa.getContigs();
283     int njobs=1;
284     if (contigs != null && contigs.length > 0) {
285       int start = 0;
286       njobs=0;
287       int width = _msa.getWidth();
288       for (int contig = 0; contig < contigs.length; contig += 3) {
289         if ((contigs[contig+1] - start) > 0) {
290           njobs++;
291         }
292         width+=contigs[contig+2];// end up with full region width (including hidden regions)
293         start = contigs[contig+1] + contigs[contig + 2];
294       }
295       if (start<width) {
296         njobs++;
297       }
298       jobs = new MsaWSJob[njobs];
299       start=0;
300       int j=0;
301       for (int contig = 0; contig < contigs.length; contig += 3) {
302         if (contigs[contig+1] - start > 0) {
303           SequenceI mseq[] = new SequenceI[msa.length];
304           for (int s = 0; s < mseq.length; s++) {
305             mseq[s] = msa[s].getSeq('-').getSubSequence(start,
306                 contigs[contig+1]);
307           }
308           if (j!=0) {
309             jobs[j] = new MsaWSJob(wsinfo.addJobPane(), mseq);
310           } else {
311             jobs[j] = new MsaWSJob(0, mseq);
312           }
313           wsinfo.setProgressName("region "+jobs[j].jobnum,jobs[j].jobnum);
314           wsinfo.setProgressText(jobs[j].jobnum, OutputHeader);
315           j++;
316         }
317         start = contigs[contig+1] + contigs[contig + 2];
318       }
319       if (start<width) {
320         SequenceI mseq[] = new SequenceI[msa.length];
321         for (int s = 0; s < mseq.length; s++) {
322           mseq[s] = msa[s].getSeq('-').getSubSequence(start,
323               width+1);
324         }
325         if (j!=0) {
326           jobs[j] = new MsaWSJob(wsinfo.addJobPane(), mseq);
327         } else {
328           jobs[j] = new MsaWSJob(0, mseq);
329         }
330         wsinfo.setProgressName("region "+jobs[j].jobnum,jobs[j].jobnum);
331         wsinfo.setProgressText(jobs[j].jobnum, OutputHeader);
332         j++;        
333       }
334     } else {
335       SequenceI mseq[] = new SequenceI[msa.length];
336       for (int s = 0; s < mseq.length; s++) {
337         mseq[s] = msa[s].getSeq('-');
338       }
339       jobs = new MsaWSJob[1];
340       wsinfo.setProgressText(OutputHeader); // ensures default text
341       jobs[0] = new MsaWSJob(0, mseq);
342     }
343   }
344   
345   public boolean isCancellable() {
346     return true;
347   }
348   
349   public void cancelJob() {
350     if (!jobComplete && jobs != null) {
351       boolean cancelled = true;
352       for (int job = 0; job < jobs.length; job++) {
353         if (jobs[job].submitted && !jobs[job].subjobComplete)
354         { 
355           String cancelledMessage = "";
356           try {
357             vamsas.objects.simple.WsJobId cancelledJob = server
358             .cancel(jobs[job].jobId);
359             if (cancelledJob.getStatus() == 2) {
360               // CANCELLED_JOB
361               cancelledMessage = "Job cancelled.";
362               jobs[job].subjobComplete = true;
363               jobs[job].result = null;
364               wsInfo.setStatus(jobs[job].jobnum, WebserviceInfo.STATE_CANCELLED_OK);
365             } else if (cancelledJob.getStatus() == 3) {
366               // VALID UNSTOPPABLE JOB
367               cancelledMessage += "Server cannot cancel this job. just close the window.\n";
368               cancelled = false;
369               wsInfo.setStatus(jobs[job].jobnum, WebserviceInfo.STATE_CANCELLED_OK);
370             }
371             
372             if (cancelledJob.getJobId() != null) {
373               cancelledMessage += ("[" + cancelledJob.getJobId() + "]");
374             }
375             
376             cancelledMessage += "\n";
377           } catch (Exception exc) {
378             cancelledMessage += ("\nProblems cancelling the job : Exception received...\n"
379                 + exc + "\n");
380             exc.printStackTrace();
381           }
382           wsInfo.setProgressText(jobs[job].jobnum, OutputHeader
383               + cancelledMessage + "\n");
384         }
385       }
386       if (cancelled) {
387         wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
388         jobComplete = true;
389       }
390     } else {
391       if (!jobComplete) {
392         wsInfo
393         .setProgressText(OutputHeader
394             + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
395       }
396     }
397   }
398   
399   public void run() {
400     
401     while (!jobComplete) {
402       int running=0;
403       int queuing=0;
404       int finished=0;
405       int error=0;
406       int serror=0;
407       for (int j=0; j<jobs.length; j++) {
408         
409         if (!jobs[j].submitted && jobs[j].seqs.getSeqs()!=null)
410           StartJob(jobs[j]);          
411         
412         if (jobs[j].submitted && !jobs[j].subjobComplete) {
413           try {
414             if ((jobs[j].result = server.getResult(jobs[j].jobId)) == null) {
415               throw (new Exception(
416               "Timed out when communicating with server\nTry again later.\n"));
417             }
418             jalview.bin.Cache.log.debug("Job "+j+" Result state " + jobs[j].result.getState()
419                 + "(ServerError=" + jobs[j].result.isServerError() + ")");
420           } catch (Exception ex) {
421             // Deal with Transaction exceptions
422             wsInfo.appendProgressText(jobs[j].jobnum, "\n" + WebServiceName
423                 + " Server exception!\n" + ex.getMessage());
424             Cache.log.warn(WebServiceName + " job(" + jobs[j].jobnum
425                 + ") Server exception: " + ex.getMessage());
426             
427             if (jobs[j].allowedServerExceptions > 0) {
428               jobs[j].allowedServerExceptions--;
429               Cache.log.debug("Sleeping after a server exception.");
430               try {
431                 Thread.sleep(5000);
432               }
433               catch (InterruptedException ex1) {
434               }
435             } else {
436               Cache.log.warn("Dropping job "+j+" "+jobs[j].jobId);
437               jobs[j].subjobComplete=true;
438               wsInfo.setStatus(jobs[j].jobnum, WebserviceInfo.STATE_STOPPED_SERVERERROR);
439             }
440           }
441           catch (OutOfMemoryError er) {   
442             jobComplete = true;
443             jobs[j].subjobComplete=true;
444             jobs[j].result=null; // may contain out of date result object
445             wsInfo.setStatus(jobs[j].jobnum,
446                 WebserviceInfo.STATE_STOPPED_ERROR);
447             JOptionPane
448             .showInternalMessageDialog(
449                 Desktop.desktop,
450                 "Out of memory handling result for job !!"
451                 + "\nSee help files for increasing Java Virtual Machine memory.",
452                 "Out of memory", JOptionPane.WARNING_MESSAGE);
453             Cache.log.error("Out of memory when retrieving Job "+j+" id:" + WsUrl+"/"+jobs[j].jobId, er);
454             System.gc();
455           }
456         }
457         if (jobs[j].result!=null) {
458           String progheader="";
459           // Parse state of job[j]
460           if (jobs[j].result.isRunning()) {
461             running++;
462             wsInfo.setStatus(jobs[j].jobnum,WebserviceInfo.STATE_RUNNING);
463           } else if (jobs[j].result.isQueued()) {
464             queuing++;
465             wsInfo.setStatus(jobs[j].jobnum,WebserviceInfo.STATE_QUEUING);
466           } else if (jobs[j].result.isFinished()) {
467             finished++;
468             jobs[j].subjobComplete = true;
469             wsInfo.setStatus(jobs[j].jobnum,WebserviceInfo.STATE_STOPPED_OK);
470           } else if (jobs[j].result.isFailed()) {
471             progheader += "Job failed.\n";
472             jobs[j].subjobComplete=true;
473             wsInfo.setStatus(jobs[j].jobnum,WebserviceInfo.STATE_STOPPED_ERROR);
474             error++;
475           } else if (jobs[j].result.isServerError()) {
476             serror++;
477             jobs[j].subjobComplete = true;
478             wsInfo.setStatus(jobs[j].jobnum,WebserviceInfo.STATE_STOPPED_SERVERERROR);
479           } else if (jobs[j].result.isBroken() || jobs[j].result.isFailed()) {
480             error++;
481             jobs[j].subjobComplete=true;
482             wsInfo.setStatus(jobs[j].jobnum,WebserviceInfo.STATE_STOPPED_ERROR);
483           }
484           // and pass on any sub-job messages to the user
485           wsInfo.setProgressText(jobs[j].jobnum, OutputHeader);
486           wsInfo.appendProgressText(jobs[j].jobnum, progheader);
487           if (jobs[j].result.getStatus() != null) {
488             wsInfo.appendProgressText(jobs[j].jobnum, jobs[j].result.getStatus());
489           }
490         } else {
491           if (jobs[j].submitted && jobs[j].subjobComplete) {
492             if (jobs[j].allowedServerExceptions==0) {
493               serror++;
494             } else if (jobs[j].result==null) {
495               error++;
496             } 
497           }
498         }
499       }
500       // Decide on overall state based on collected jobs[] states
501       if (running>0) {
502         wsInfo.setStatus(WebserviceInfo.STATE_RUNNING);
503       } else if (queuing>0) {
504         wsInfo.setStatus(WebserviceInfo.STATE_QUEUING);
505       } else {
506         jobComplete=true;
507         if (finished>0) {
508           wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_OK);
509         } else if (error>0) {
510           wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);          
511         } else if (serror>0) {
512           wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
513         }
514       }
515       if (!jobComplete) {
516         try {
517           Thread.sleep(5000);
518         }
519         catch (InterruptedException e) {
520           Cache.log.debug("Interrupted sleep waiting for next job poll.",e);
521         }
522         // System.out.println("I'm alive "+alTitle);
523       }
524     }
525     if (jobComplete) {
526       parseResult(); // tidy up and make results available to user
527     }
528   }
529   
530   void StartJob(MsaWSJob j) {
531     if (j.submitted) {
532       if (Cache.log.isDebugEnabled()) {
533         Cache.log.debug("Tried to submit an already submitted job "+j.jobId);
534       }
535       return;
536     }
537     if (j.seqs.getSeqs()==null) {
538       // special case - selection consisted entirely of empty sequences...
539       j.submitted=true;
540       j.result=new MsaResult();
541       j.result.setFinished(true);
542       j.result.setStatus("Empty Alignment Job");
543       j.result.setMsa(null);
544     }
545     try {
546       vamsas.objects.simple.WsJobId jobsubmit = server.align(j.seqs);
547       
548       if ((jobsubmit != null) && (jobsubmit.getStatus() == 1)) {
549         j.jobId = jobsubmit.getJobId();
550         j.submitted=true;
551         j.subjobComplete = false;
552         // System.out.println(WsURL + " Job Id '" + jobId + "'");
553       } else {
554         if (jobsubmit == null) {
555           throw new Exception(
556               "Server at "
557               + WsUrl
558               + " returned null object, it probably cannot be contacted. Try again later ?");
559         }
560         
561         throw new Exception(jobsubmit.getJobId());
562       }
563     } catch (Exception e) {
564       // TODO: JBPNote catch timeout or other fault types explicitly
565       // For unexpected errors
566       System.err
567       .println(WebServiceName
568           + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
569           + "When contacting Server:" + WsUrl + "\n"
570           + e.toString() + "\n");
571       j.allowedServerExceptions = 0;
572       wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
573       wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_SERVERERROR);
574       wsInfo
575       .appendProgressText(
576           j.jobnum,
577           "Failed to submit sequences for alignment.\n"
578           + "It is most likely that there is a problem with the server.\n"
579           + "Just close the window\n");
580       
581       // e.printStackTrace(); // TODO: JBPNote DEBUG
582     }
583   }
584   
585   private jalview.datamodel.Sequence[] getVamsasAlignment(
586       vamsas.objects.simple.Alignment valign) {
587     vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs();
588     jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs.length];
589     
590     for (int i = 0, j = seqs.length; i < j; i++) {
591       msa[i] = new jalview.datamodel.Sequence(seqs[i].getId(), seqs[i]
592                                                                     .getSeq());
593     }
594     
595     return msa;
596   }
597   
598   void parseResult() {
599     try {
600       for (int j=0; j<jobs.length; j++) {
601         if (jobs[j].submitted && jobs[j].subjobComplete) {
602           if (jobs[j].result!=null) {
603             vamsas.objects.simple.Alignment valign = jobs[j].result.getMsa();
604             if (valign!= null) {
605               wsInfo.appendProgressText(jobs[j].jobnum,"\nAlignment Object Method Notes\n");
606               String[] lines = valign.getMethod();
607               for (int line = 0; line < lines.length; line++) {
608                 wsInfo.appendProgressText(jobs[j].jobnum, lines[line] + "\n");
609               }
610               // JBPNote The returned files from a webservice could be
611               //  hidden behind icons in the monitor window that,
612               // when clicked, pop up their corresponding data
613             }
614           }
615         }
616       }
617     }
618     catch (Exception ex) {
619       
620       Cache.log.error("Unexpected exception when processing results for "+alTitle,ex);
621       wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
622     }
623     
624     wsInfo.showResultsNewFrame
625     .addActionListener(new java.awt.event.ActionListener() {
626       public void actionPerformed(
627           java.awt.event.ActionEvent evt) {
628         displayResults(true);
629       }
630     });
631     wsInfo.mergeResults
632     .addActionListener(new java.awt.event.ActionListener() {
633       public void actionPerformed(
634           java.awt.event.ActionEvent evt) {
635         displayResults(false);
636       }
637     });
638     wsInfo.setResultsReady();
639   }
640   void displayResults(boolean newFrame) {
641     int contigs[] = input.getContigs();
642     SeqCigar[] seqs = input.getSequences();
643     SequenceI[] alignment = new SequenceI[seqs.length];
644     ColumnSelection columnselection = new ColumnSelection();
645     Vector alorders=new Vector();
646     if (contigs != null && contigs.length > 0) {
647       int cshift=0;
648       int start = 0;
649       int nwidth = 0;
650       int j=0;
651       for (int contig = 0; contig < contigs.length; contig += 3) {
652         if (cshift+contigs[contig] - start > 0) {
653           Object[] subalg =  jobs[j++].getAlignment();
654           alorders.add(subalg[1]);
655           SequenceI mseq[] = (SequenceI[]) subalg[0];
656           int width = mseq[0].getLength();
657           for (int s = 0; s < mseq.length; s++) {
658             if (alignment[s]==null) {
659               alignment[s] = mseq[s];
660             } else {
661               alignment[s].setSequence(alignment[s].getSequence()+mseq[s].getSequence());
662               if (mseq[s].getStart()<=mseq[s].getEnd())
663                 alignment[s].setEnd(mseq[s].getEnd());
664               ((AlignmentOrder) subalg[1]).updateSequence(mseq[s], alignment[s]);
665             }
666           }
667           nwidth+=width;
668         }
669         // advance to begining of visible region
670         start = cshift+contigs[contig] + contigs[contig + 2];
671         // add hidden segment to right of next region
672         for (int s=0; s<seqs.length; s++) {
673           SequenceI hseq = seqs[s].getSeq('-').getSubSequence(cshift+contigs[contig], start);
674           if (alignment[s]==null) {
675             alignment[s] = hseq;
676           } else {
677             alignment[s].setSequence(alignment[s].getSequence()+hseq.getSequence());
678             if (hseq.getEnd()>=hseq.getStart())
679               alignment[s].setEnd(hseq.getEnd());
680           }
681         }
682         // mark hidden segment as hidden in the new alignment
683         columnselection.hideColumns(nwidth, nwidth+contigs[contig+2]-1);
684         nwidth+=contigs[contig+2];
685         cshift+=contigs[contig+2];
686       }
687       // Do final job - if it exists
688       if (j<jobs.length && jobs[j].hasResults()) {
689         Object[] subalg =  jobs[j].getAlignment();
690         alorders.add(subalg[1]);
691         SequenceI mseq[] = (SequenceI[]) subalg[0];
692         int width = mseq[0].getLength();
693         for (int s = 0; s < mseq.length; s++) {
694           if (alignment[s]==null) {
695             alignment[s] = mseq[s];
696           } else {
697             alignment[s].setSequence(alignment[s].getSequence()+mseq[s].getSequence());
698             if (mseq[s].getEnd()>=mseq[s].getStart())
699               alignment[s].setEnd(mseq[s].getEnd());
700             ((AlignmentOrder) subalg[1]).updateSequence(mseq[s], alignment[s]);
701           }
702         }
703         nwidth+=width;  
704       }
705     } else {
706       if (jobs[0].hasResults()) {
707         Object[] alg = jobs[0].getAlignment();
708         alignment = (SequenceI[]) alg[0];
709         alorders.add(alg[1]);
710       } else {
711         alignment = SeqCigar.createAlignmentSequences(seqs, '-', columnselection,null);
712       }
713     }
714     Alignment al = new Alignment(alignment);
715     if (dataset != null) {
716       al.setDataset(dataset);
717     }
718     
719     if (newFrame) {
720       // TODO: JBPNote Should also rename the query sequence
721       // sometime...
722       AlignFrame af = new AlignFrame(al,columnselection);
723       
724       // >>>This is a fix for the moment, until a better solution is
725       // found!!<<<
726       af.getFeatureRenderer().transferSettings(
727           alignFrame.getFeatureRenderer());
728       if (alorders.size()>0) {
729         if (alorders.size()==1) {
730           af.addSortByOrderMenuItem(WebServiceName + " Ordering",
731               (AlignmentOrder) alorders.get(0));
732         } else {
733           // construct a non-redundant ordering set
734           Vector names=new Vector();
735           for (int i=0,l=alorders.size(); i<l; i++) {
736             String orderName = new String("Region "+i);
737             int j=i+1;
738             int r=l;
739             while (j<l) {
740               if (((AlignmentOrder) alorders.get(i)).equals(((AlignmentOrder) alorders.get(j)))) {
741                 alorders.remove(j); l--;
742                 orderName+=","+j;
743               } else
744                 j++;
745             }
746             
747             if (i==0 && j==1)
748               names.add(new String(""));
749             else
750               names.add(orderName);
751           }
752           for (int i=0,l=alorders.size(); i<l; i++) {
753             af.addSortByOrderMenuItem(WebServiceName + ((String) names.get(i))+" Ordering",
754                 (AlignmentOrder) alorders.get(i));              
755           }
756         }
757       }
758       
759       Desktop.addInternalFrame(af, alTitle,
760           AlignFrame.NEW_WINDOW_WIDTH,
761           AlignFrame.NEW_WINDOW_HEIGHT);
762       
763     } else {
764       System.out.println("MERGE WITH OLD FRAME");
765       
766     }
767   }
768 }