First patch for * JAL-493
[jalview.git] / src / jalview / ws / MsaWSThread.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.5)\r
3  * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle\r
4  * \r
5  * This file is part of Jalview.\r
6  * \r
7  * Jalview is free software: you can redistribute it and/or\r
8  * modify it under the terms of the GNU General Public License \r
9  * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\r
10  * \r
11  * Jalview is distributed in the hope that it will be useful, but \r
12  * WITHOUT ANY WARRANTY; without even the implied warranty \r
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR \r
14  * PURPOSE.  See the GNU General Public License for more details.\r
15  * \r
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.\r
17  */\r
18 package jalview.ws;\r
19 \r
20 import java.util.*;\r
21 \r
22 import jalview.analysis.*;\r
23 import jalview.bin.*;\r
24 import jalview.datamodel.*;\r
25 import jalview.gui.*;\r
26 import vamsas.objects.simple.MsaResult;\r
27 \r
28 /**\r
29  * <p>\r
30  * Title:\r
31  * </p>\r
32  * \r
33  * <p>\r
34  * Description:\r
35  * </p>\r
36  * \r
37  * <p>\r
38  * Copyright: Copyright (c) 2004\r
39  * </p>\r
40  * \r
41  * <p>\r
42  * Company: Dundee University\r
43  * </p>\r
44  * \r
45  * @author not attributable\r
46  * @version 1.0\r
47  */\r
48 class MsaWSThread extends JWS1Thread implements WSClientI\r
49 {\r
50   boolean submitGaps = false; // pass sequences including gaps to alignment\r
51 \r
52   // service\r
53 \r
54   boolean preserveOrder = true; // and always store and recover sequence\r
55 \r
56   // order\r
57 \r
58   class MsaWSJob extends WSJob\r
59   {\r
60     // hold special input for this\r
61     vamsas.objects.simple.SequenceSet seqs = new vamsas.objects.simple.SequenceSet();\r
62 \r
63     /**\r
64      * MsaWSJob\r
65      * \r
66      * @param jobNum\r
67      *          int\r
68      * @param jobId\r
69      *          String\r
70      */\r
71     public MsaWSJob(int jobNum, SequenceI[] inSeqs)\r
72     {\r
73       this.jobnum = jobNum;\r
74       if (!prepareInput(inSeqs, 2))\r
75       {\r
76         submitted = true;\r
77         subjobComplete = true;\r
78         result = new MsaResult();\r
79         result.setFinished(true);\r
80         result.setStatus("Job never ran - input returned to user.");\r
81       }\r
82 \r
83     }\r
84 \r
85     Hashtable SeqNames = new Hashtable();\r
86 \r
87     Vector emptySeqs = new Vector();\r
88 \r
89     /**\r
90      * prepare input sequences for MsaWS service\r
91      * \r
92      * @param seqs\r
93      *          jalview sequences to be prepared\r
94      * @param minlen\r
95      *          minimum number of residues required for this MsaWS service\r
96      * @return true if seqs contains sequences to be submitted to service.\r
97      */\r
98     private boolean prepareInput(SequenceI[] seqs, int minlen)\r
99     {\r
100       int nseqs = 0;\r
101       if (minlen < 0)\r
102       {\r
103         throw new Error(\r
104                 "Implementation error: minlen must be zero or more.");\r
105       }\r
106       for (int i = 0; i < seqs.length; i++)\r
107       {\r
108         if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)\r
109         {\r
110           nseqs++;\r
111         }\r
112       }\r
113       boolean valid = nseqs > 1; // need at least two seqs\r
114       vamsas.objects.simple.Sequence[] seqarray = (valid) ? new vamsas.objects.simple.Sequence[nseqs]\r
115               : null;\r
116       for (int i = 0, n = 0; i < seqs.length; i++)\r
117       {\r
118 \r
119         String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same\r
120         // for\r
121         // any\r
122         // subjob\r
123         SeqNames.put(newname, jalview.analysis.SeqsetUtils\r
124                 .SeqCharacterHash(seqs[i]));\r
125         if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)\r
126         {\r
127           seqarray[n] = new vamsas.objects.simple.Sequence();\r
128           seqarray[n].setId(newname);\r
129           seqarray[n++].setSeq((submitGaps) ? seqs[i].getSequenceAsString()\r
130                   : AlignSeq.extractGaps(jalview.util.Comparison.GapChars,\r
131                           seqs[i].getSequenceAsString()));\r
132         }\r
133         else\r
134         {\r
135           String empty = null;\r
136           if (seqs[i].getEnd() >= seqs[i].getStart())\r
137           {\r
138             empty = (submitGaps) ? seqs[i].getSequenceAsString() : AlignSeq\r
139                     .extractGaps(jalview.util.Comparison.GapChars, seqs[i]\r
140                             .getSequenceAsString());\r
141           }\r
142           emptySeqs.add(new String[]\r
143           { newname, empty });\r
144         }\r
145       }\r
146       this.seqs = new vamsas.objects.simple.SequenceSet();\r
147       this.seqs.setSeqs(seqarray);\r
148       return valid;\r
149     }\r
150 \r
151     /**\r
152      * \r
153      * @return true if getAlignment will return a valid alignment result.\r
154      */\r
155     public boolean hasResults()\r
156     {\r
157       if (subjobComplete && result != null && result.isFinished()\r
158               && ((MsaResult) result).getMsa() != null\r
159               && ((MsaResult) result).getMsa().getSeqs() != null)\r
160       {\r
161         return true;\r
162       }\r
163       return false;\r
164     }\r
165 \r
166     public Object[] getAlignment()\r
167     {\r
168 \r
169       if (result != null && result.isFinished())\r
170       {\r
171         SequenceI[] alseqs = null;\r
172         char alseq_gapchar = '-';\r
173         int alseq_l = 0;\r
174         if (((MsaResult) result).getMsa() != null)\r
175         {\r
176           alseqs = getVamsasAlignment(((MsaResult) result).getMsa());\r
177           alseq_gapchar = ((MsaResult) result).getMsa().getGapchar()\r
178                   .charAt(0);\r
179           alseq_l = alseqs.length;\r
180         }\r
181         if (emptySeqs.size() > 0)\r
182         {\r
183           SequenceI[] t_alseqs = new SequenceI[alseq_l + emptySeqs.size()];\r
184           // get width\r
185           int i, w = 0;\r
186           if (alseq_l > 0)\r
187           {\r
188             for (i = 0, w = alseqs[0].getLength(); i < alseq_l; i++)\r
189             {\r
190               if (w < alseqs[i].getLength())\r
191               {\r
192                 w = alseqs[i].getLength();\r
193               }\r
194               t_alseqs[i] = alseqs[i];\r
195               alseqs[i] = null;\r
196             }\r
197           }\r
198           // check that aligned width is at least as wide as emptySeqs width.\r
199           int ow = w, nw = w;\r
200           for (i = 0, w = emptySeqs.size(); i < w; i++)\r
201           {\r
202             String[] es = (String[]) emptySeqs.get(i);\r
203             if (es != null && es[1] != null)\r
204             {\r
205               int sw = es[1].length();\r
206               if (nw < sw)\r
207               {\r
208                 nw = sw;\r
209               }\r
210             }\r
211           }\r
212           // make a gapped string.\r
213           StringBuffer insbuff = new StringBuffer(w);\r
214           for (i = 0; i < nw; i++)\r
215           {\r
216             insbuff.append(alseq_gapchar);\r
217           }\r
218           if (ow < nw)\r
219           {\r
220             for (i = 0; i < alseq_l; i++)\r
221             {\r
222               int sw = t_alseqs[i].getLength();\r
223               if (nw > sw)\r
224               {\r
225                 // pad at end\r
226                 alseqs[i].setSequence(t_alseqs[i].getSequenceAsString()\r
227                         + insbuff.substring(0, sw - nw));\r
228               }\r
229             }\r
230           }\r
231           for (i = 0, w = emptySeqs.size(); i < w; i++)\r
232           {\r
233             String[] es = (String[]) emptySeqs.get(i);\r
234             if (es[1] == null)\r
235             {\r
236               t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(es[0],\r
237                       insbuff.toString(), 1, 0);\r
238             }\r
239             else\r
240             {\r
241               if (es[1].length() < nw)\r
242               {\r
243                 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(\r
244                         es[0],\r
245                         es[1] + insbuff.substring(0, nw - es[1].length()),\r
246                         1, 1 + es[1].length());\r
247               }\r
248               else\r
249               {\r
250                 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(\r
251                         es[0], es[1]);\r
252               }\r
253             }\r
254           }\r
255           alseqs = t_alseqs;\r
256         }\r
257         AlignmentOrder msaorder = new AlignmentOrder(alseqs);\r
258         // always recover the order - makes parseResult()'s life easier.\r
259         jalview.analysis.AlignmentSorter.recoverOrder(alseqs);\r
260         // account for any missing sequences\r
261         jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs);\r
262         return new Object[]\r
263         { alseqs, msaorder };\r
264       }\r
265       return null;\r
266     }\r
267 \r
268     /**\r
269      * mark subjob as cancelled and set result object appropriatly\r
270      */\r
271     void cancel()\r
272     {\r
273       cancelled = true;\r
274       subjobComplete = true;\r
275       result = null;\r
276     }\r
277 \r
278     /**\r
279      * \r
280      * @return boolean true if job can be submitted.\r
281      */\r
282     public boolean hasValidInput()\r
283     {\r
284       if (seqs.getSeqs() != null)\r
285       {\r
286         return true;\r
287       }\r
288       return false;\r
289     }\r
290   }\r
291 \r
292   String alTitle; // name which will be used to form new alignment window.\r
293 \r
294   Alignment dataset; // dataset to which the new alignment will be\r
295 \r
296   // associated.\r
297 \r
298   ext.vamsas.MuscleWS server = null;\r
299 \r
300   /**\r
301    * set basic options for this (group) of Msa jobs\r
302    * \r
303    * @param subgaps\r
304    *          boolean\r
305    * @param presorder\r
306    *          boolean\r
307    */\r
308   MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,\r
309           WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,\r
310           AlignmentView alview, String wsname, boolean subgaps,\r
311           boolean presorder)\r
312   {\r
313     super(alFrame, wsinfo, alview, wsname, wsUrl);\r
314     this.server = server;\r
315     this.submitGaps = subgaps;\r
316     this.preserveOrder = presorder;\r
317   }\r
318 \r
319   /**\r
320    * create one or more Msa jobs to align visible seuqences in _msa\r
321    * \r
322    * @param title\r
323    *          String\r
324    * @param _msa\r
325    *          AlignmentView\r
326    * @param subgaps\r
327    *          boolean\r
328    * @param presorder\r
329    *          boolean\r
330    * @param seqset\r
331    *          Alignment\r
332    */\r
333   MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,\r
334           WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,\r
335           String wsname, String title, AlignmentView _msa, boolean subgaps,\r
336           boolean presorder, Alignment seqset)\r
337   {\r
338     this(server, wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder);\r
339     OutputHeader = wsInfo.getProgressText();\r
340     alTitle = title;\r
341     dataset = seqset;\r
342 \r
343     SequenceI[][] conmsa = _msa.getVisibleContigs('-');\r
344     if (conmsa != null)\r
345     {\r
346       int njobs = conmsa.length;\r
347       jobs = new MsaWSJob[njobs];\r
348       for (int j = 0; j < njobs; j++)\r
349       {\r
350         if (j != 0)\r
351         {\r
352           jobs[j] = new MsaWSJob(wsinfo.addJobPane(), conmsa[j]);\r
353         }\r
354         else\r
355         {\r
356           jobs[j] = new MsaWSJob(0, conmsa[j]);\r
357         }\r
358         if (njobs > 0)\r
359         {\r
360           wsinfo\r
361                   .setProgressName("region " + jobs[j].jobnum,\r
362                           jobs[j].jobnum);\r
363         }\r
364         wsinfo.setProgressText(jobs[j].jobnum, OutputHeader);\r
365       }\r
366     }\r
367   }\r
368 \r
369   public boolean isCancellable()\r
370   {\r
371     return true;\r
372   }\r
373 \r
374   public void cancelJob()\r
375   {\r
376     if (!jobComplete && jobs != null)\r
377     {\r
378       boolean cancelled = true;\r
379       for (int job = 0; job < jobs.length; job++)\r
380       {\r
381         if (jobs[job].submitted && !jobs[job].subjobComplete)\r
382         {\r
383           String cancelledMessage = "";\r
384           try\r
385           {\r
386             vamsas.objects.simple.WsJobId cancelledJob = server\r
387                     .cancel(jobs[job].jobId);\r
388             if (cancelledJob.getStatus() == 2)\r
389             {\r
390               // CANCELLED_JOB\r
391               cancelledMessage = "Job cancelled.";\r
392               ((MsaWSJob) jobs[job]).cancel();\r
393               wsInfo.setStatus(jobs[job].jobnum,\r
394                       WebserviceInfo.STATE_CANCELLED_OK);\r
395             }\r
396             else if (cancelledJob.getStatus() == 3)\r
397             {\r
398               // VALID UNSTOPPABLE JOB\r
399               cancelledMessage += "Server cannot cancel this job. just close the window.\n";\r
400               cancelled = false;\r
401               // wsInfo.setStatus(jobs[job].jobnum,\r
402               // WebserviceInfo.STATE_RUNNING);\r
403             }\r
404 \r
405             if (cancelledJob.getJobId() != null)\r
406             {\r
407               cancelledMessage += ("[" + cancelledJob.getJobId() + "]");\r
408             }\r
409 \r
410             cancelledMessage += "\n";\r
411           } catch (Exception exc)\r
412           {\r
413             cancelledMessage += ("\nProblems cancelling the job : Exception received...\n"\r
414                     + exc + "\n");\r
415             Cache.log.warn(\r
416                     "Exception whilst cancelling " + jobs[job].jobId, exc);\r
417           }\r
418           wsInfo.setProgressText(jobs[job].jobnum, OutputHeader\r
419                   + cancelledMessage + "\n");\r
420         }\r
421       }\r
422       if (cancelled)\r
423       {\r
424         wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);\r
425         jobComplete = true;\r
426       }\r
427       this.interrupt(); // kick thread to update job states.\r
428     }\r
429     else\r
430     {\r
431       if (!jobComplete)\r
432       {\r
433         wsInfo\r
434                 .setProgressText(OutputHeader\r
435                         + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");\r
436       }\r
437     }\r
438   }\r
439 \r
440   public void pollJob(AWsJob job) throws Exception\r
441   {\r
442     ((MsaWSJob) job).result = server.getResult(((MsaWSJob) job).jobId);\r
443   }\r
444 \r
445   public void StartJob(AWsJob job)\r
446   {\r
447     if (!(job instanceof MsaWSJob))\r
448     {\r
449       throw new Error("StartJob(MsaWSJob) called on a WSJobInstance "\r
450               + job.getClass());\r
451     }\r
452     MsaWSJob j = (MsaWSJob) job;\r
453     if (j.submitted)\r
454     {\r
455       if (Cache.log.isDebugEnabled())\r
456       {\r
457         Cache.log.debug("Tried to submit an already submitted job "\r
458                 + j.jobId);\r
459       }\r
460       return;\r
461     }\r
462     if (j.seqs.getSeqs() == null)\r
463     {\r
464       // special case - selection consisted entirely of empty sequences...\r
465       j.submitted = true;\r
466       j.result = new MsaResult();\r
467       j.result.setFinished(true);\r
468       j.result.setStatus("Empty Alignment Job");\r
469       ((MsaResult) j.result).setMsa(null);\r
470     }\r
471     try\r
472     {\r
473       vamsas.objects.simple.WsJobId jobsubmit = server.align(j.seqs);\r
474 \r
475       if ((jobsubmit != null) && (jobsubmit.getStatus() == 1))\r
476       {\r
477         j.jobId = jobsubmit.getJobId();\r
478         j.submitted = true;\r
479         j.subjobComplete = false;\r
480         // System.out.println(WsURL + " Job Id '" + jobId + "'");\r
481       }\r
482       else\r
483       {\r
484         if (jobsubmit == null)\r
485         {\r
486           throw new Exception(\r
487                   "Server at "\r
488                           + WsUrl\r
489                           + " returned null object, it probably cannot be contacted. Try again later ?");\r
490         }\r
491 \r
492         throw new Exception(jobsubmit.getJobId());\r
493       }\r
494     } catch (Exception e)\r
495     {\r
496       // TODO: JBPNote catch timeout or other fault types explicitly\r
497       // For unexpected errors\r
498       System.err\r
499               .println(WebServiceName\r
500                       + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"\r
501                       + "When contacting Server:" + WsUrl + "\n"\r
502                       + e.toString() + "\n");\r
503       j.allowedServerExceptions = 0;\r
504       wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);\r
505       wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_SERVERERROR);\r
506       wsInfo\r
507               .appendProgressText(\r
508                       j.jobnum,\r
509                       "Failed to submit sequences for alignment.\n"\r
510                               + "It is most likely that there is a problem with the server.\n"\r
511                               + "Just close the window\n");\r
512 \r
513       // e.printStackTrace(); // TODO: JBPNote DEBUG\r
514     }\r
515   }\r
516 \r
517   private jalview.datamodel.Sequence[] getVamsasAlignment(\r
518           vamsas.objects.simple.Alignment valign)\r
519   {\r
520     // TODO: refactor to helper class for vamsas.objects.simple objects\r
521     vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs();\r
522     jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs.length];\r
523 \r
524     for (int i = 0, j = seqs.length; i < j; i++)\r
525     {\r
526       msa[i] = new jalview.datamodel.Sequence(seqs[i].getId(), seqs[i]\r
527               .getSeq());\r
528     }\r
529 \r
530     return msa;\r
531   }\r
532 \r
533   public void parseResult()\r
534   {\r
535     int results = 0; // number of result sets received\r
536     JobStateSummary finalState = new JobStateSummary();\r
537     try\r
538     {\r
539       for (int j = 0; j < jobs.length; j++)\r
540       {\r
541         finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);\r
542         if (jobs[j].submitted && jobs[j].subjobComplete\r
543                 && jobs[j].hasResults())\r
544         {\r
545           results++;\r
546           vamsas.objects.simple.Alignment valign =  ((MsaResult)((MsaWSJob) jobs[j]).result)\r
547                   .getMsa();\r
548           if (valign != null)\r
549           {\r
550             wsInfo.appendProgressText(jobs[j].jobnum,\r
551                     "\nAlignment Object Method Notes\n");\r
552             String[] lines = valign.getMethod();\r
553             for (int line = 0; line < lines.length; line++)\r
554             {\r
555               wsInfo.appendProgressText(jobs[j].jobnum, lines[line] + "\n");\r
556             }\r
557             // JBPNote The returned files from a webservice could be\r
558             // hidden behind icons in the monitor window that,\r
559             // when clicked, pop up their corresponding data\r
560           }\r
561         }\r
562       }\r
563     } catch (Exception ex)\r
564     {\r
565 \r
566       Cache.log.error("Unexpected exception when processing results for "\r
567               + alTitle, ex);\r
568       wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);\r
569     }\r
570     if (results > 0)\r
571     {\r
572       wsInfo.showResultsNewFrame\r
573               .addActionListener(new java.awt.event.ActionListener()\r
574               {\r
575                 public void actionPerformed(java.awt.event.ActionEvent evt)\r
576                 {\r
577                   displayResults(true);\r
578                 }\r
579               });\r
580       wsInfo.mergeResults\r
581               .addActionListener(new java.awt.event.ActionListener()\r
582               {\r
583                 public void actionPerformed(java.awt.event.ActionEvent evt)\r
584                 {\r
585                   displayResults(false);\r
586                 }\r
587               });\r
588       wsInfo.setResultsReady();\r
589     }\r
590     else\r
591     {\r
592       wsInfo.setFinishedNoResults();\r
593     }\r
594   }\r
595 \r
596   void displayResults(boolean newFrame)\r
597   {\r
598     // view input or result data for each block\r
599     Vector alorders = new Vector();\r
600     SequenceI[][] results = new SequenceI[jobs.length][];\r
601     AlignmentOrder[] orders = new AlignmentOrder[jobs.length];\r
602     for (int j = 0; j < jobs.length; j++)\r
603     {\r
604       if (jobs[j].hasResults())\r
605       {\r
606         Object[] res = ((MsaWSJob) jobs[j]).getAlignment();\r
607         alorders.add(res[1]);\r
608         results[j] = (SequenceI[]) res[0];\r
609         orders[j] = (AlignmentOrder) res[1];\r
610 \r
611         // SequenceI[] alignment = input.getUpdated\r
612       }\r
613       else\r
614       {\r
615         results[j] = null;\r
616       }\r
617     }\r
618     Object[] newview = input.getUpdatedView(results, orders, getGapChar());\r
619     // trash references to original result data\r
620     for (int j = 0; j < jobs.length; j++)\r
621     {\r
622       results[j] = null;\r
623       orders[j] = null;\r
624     }\r
625     SequenceI[] alignment = (SequenceI[]) newview[0];\r
626     ColumnSelection columnselection = (ColumnSelection) newview[1];\r
627     Alignment al = new Alignment(alignment);\r
628     // TODO: add 'provenance' property to alignment from the method notes\r
629     // accompanying each subjob\r
630     if (dataset != null)\r
631     {\r
632       al.setDataset(dataset);\r
633     }\r
634 \r
635     propagateDatasetMappings(al);\r
636     // JBNote- TODO: warn user if a block is input rather than aligned data ?\r
637 \r
638     if (newFrame)\r
639     {\r
640       AlignFrame af = new AlignFrame(al, columnselection,\r
641               AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);\r
642 \r
643       // initialise with same renderer settings as in parent alignframe.\r
644       af.getFeatureRenderer().transferSettings(this.featureSettings);\r
645       // update orders\r
646       if (alorders.size() > 0)\r
647       {\r
648         if (alorders.size() == 1)\r
649         {\r
650           af.addSortByOrderMenuItem(WebServiceName + " Ordering",\r
651                   (AlignmentOrder) alorders.get(0));\r
652         }\r
653         else\r
654         {\r
655           // construct a non-redundant ordering set\r
656           Vector names = new Vector();\r
657           for (int i = 0, l = alorders.size(); i < l; i++)\r
658           {\r
659             String orderName = new String(" Region " + i);\r
660             int j = i + 1;\r
661 \r
662             while (j < l)\r
663             {\r
664               if (((AlignmentOrder) alorders.get(i))\r
665                       .equals(((AlignmentOrder) alorders.get(j))))\r
666               {\r
667                 alorders.remove(j);\r
668                 l--;\r
669                 orderName += "," + j;\r
670               }\r
671               else\r
672               {\r
673                 j++;\r
674               }\r
675             }\r
676 \r
677             if (i == 0 && j == 1)\r
678             {\r
679               names.add(new String(""));\r
680             }\r
681             else\r
682             {\r
683               names.add(orderName);\r
684             }\r
685           }\r
686           for (int i = 0, l = alorders.size(); i < l; i++)\r
687           {\r
688             af.addSortByOrderMenuItem(WebServiceName\r
689                     + ((String) names.get(i)) + " Ordering",\r
690                     (AlignmentOrder) alorders.get(i));\r
691           }\r
692         }\r
693       }\r
694 \r
695       Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,\r
696               AlignFrame.DEFAULT_HEIGHT);\r
697 \r
698     }\r
699     else\r
700     {\r
701       System.out.println("MERGE WITH OLD FRAME");\r
702       // TODO: modify alignment in original frame, replacing old for new\r
703       // alignment using the commands.EditCommand model to ensure the update can\r
704       // be undone\r
705     }\r
706   }\r
707 \r
708   public boolean canMergeResults()\r
709   {\r
710     return false;\r
711   }\r
712 }\r