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