0c345184516c82dcb37b2a3024d5d4dbeafd5ada
[jalview.git] / src / jalview / ws / SeqSearchWSThread.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.gui.*;\r
27 import jalview.io.NewickFile;\r
28 import vamsas.objects.simple.MsaResult;\r
29 import vamsas.objects.simple.SeqSearchResult;\r
30 \r
31 /**\r
32  * <p>\r
33  * Title:\r
34  * </p>\r
35  * \r
36  * <p>\r
37  * Description:\r
38  * </p>\r
39  * \r
40  * <p>\r
41  * Copyright: Copyright (c) 2004\r
42  * </p>\r
43  * \r
44  * <p>\r
45  * Company: Dundee University\r
46  * </p>\r
47  * \r
48  * @author not attributable\r
49  * @version 1.0\r
50  */\r
51 class SeqSearchWSThread extends WSThread implements WSClientI\r
52 {\r
53   String dbs = null;\r
54 \r
55   boolean profile = false;\r
56 \r
57   class SeqSearchWSJob extends WSThread.WSJob\r
58   {\r
59     // hold special input for this\r
60     vamsas.objects.simple.SequenceSet seqs = new vamsas.objects.simple.SequenceSet();\r
61 \r
62     /**\r
63      * MsaWSJob\r
64      * \r
65      * @param jobNum\r
66      *                int\r
67      * @param jobId\r
68      *                String\r
69      */\r
70     public SeqSearchWSJob(int jobNum, SequenceI[] inSeqs)\r
71     {\r
72       this.jobnum = jobNum;\r
73       if (!prepareInput(inSeqs, 2))\r
74       {\r
75         submitted = true;\r
76         subjobComplete = true;\r
77         result = new MsaResult();\r
78         result.setFinished(true);\r
79         result.setStatus("Job never ran - input returned to user.");\r
80       }\r
81 \r
82     }\r
83 \r
84     Hashtable SeqNames = new Hashtable();\r
85 \r
86     Vector emptySeqs = new Vector();\r
87 \r
88     /**\r
89      * prepare input sequences for service\r
90      * \r
91      * @param seqs\r
92      *                jalview sequences to be prepared\r
93      * @param minlen\r
94      *                minimum number of residues required for this MsaWS service\r
95      * @return true if seqs contains sequences to be submitted to service.\r
96      */\r
97     private boolean prepareInput(SequenceI[] seqs, int minlen)\r
98     {\r
99       int nseqs = 0;\r
100       if (minlen < 0)\r
101       {\r
102         throw new Error(\r
103                 "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 one sequence for valid input\r
113                                   // TODO: generalise\r
114       vamsas.objects.simple.Sequence[] seqarray = (valid) ? new vamsas.objects.simple.Sequence[nseqs]\r
115               : null;\r
116       boolean submitGaps = (nseqs == 1) ? false : true; // profile is submitted\r
117                                                         // with gaps\r
118       for (int i = 0, n = 0; i < seqs.length; i++)\r
119       {\r
120 \r
121         String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same\r
122         // for\r
123         // any\r
124         // subjob\r
125         SeqNames.put(newname, jalview.analysis.SeqsetUtils\r
126                 .SeqCharacterHash(seqs[i]));\r
127         if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)\r
128         {\r
129           seqarray[n] = new vamsas.objects.simple.Sequence();\r
130           seqarray[n].setId(newname);\r
131           seqarray[n++].setSeq((submitGaps) ? seqs[i].getSequenceAsString()\r
132                   : AlignSeq.extractGaps(jalview.util.Comparison.GapChars,\r
133                           seqs[i].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() : AlignSeq\r
141                     .extractGaps(jalview.util.Comparison.GapChars, seqs[i]\r
142                             .getSequenceAsString());\r
143           }\r
144           emptySeqs.add(new String[]\r
145           { newname, empty });\r
146         }\r
147       }\r
148       if (submitGaps)\r
149       {\r
150         // almost certainly have to remove gapped columns here\r
151       }\r
152       this.seqs = new vamsas.objects.simple.SequenceSet();\r
153       this.seqs.setSeqs(seqarray);\r
154       return valid;\r
155     }\r
156 \r
157     /**\r
158      * \r
159      * @return true if getAlignment will return a valid alignment result.\r
160      */\r
161     public boolean hasResults()\r
162     {\r
163       if (subjobComplete\r
164               && result != null\r
165               && result.isFinished()\r
166               && ((SeqSearchResult) result).getAlignment() != null\r
167               && ((SeqSearchResult) result).getAlignment().getSeqs() != null)\r
168       {\r
169         return true;\r
170       }\r
171       return false;\r
172     }\r
173 \r
174     /**\r
175      * return sequence search results for display\r
176      * \r
177      * @return null or { Alignment(+features and annotation), NewickFile)}\r
178      */\r
179     public Object[] getAlignment(Alignment dataset, Hashtable featureColours)\r
180     {\r
181 \r
182       if (result != null && result.isFinished())\r
183       {\r
184         SequenceI[] alseqs = null;\r
185         // char alseq_gapchar = '-';\r
186         // int alseq_l = 0;\r
187         if (((SeqSearchResult) result).getAlignment() != null)\r
188         {\r
189           alseqs = getVamsasAlignment(((SeqSearchResult) result)\r
190                   .getAlignment());\r
191           // alseq_gapchar = ( (SeqSearchResult)\r
192           // result).getAlignment().getGapchar().charAt(0);\r
193           // alseq_l = alseqs.length;\r
194         }\r
195         /**\r
196          * what has to be done. 1 - annotate returned alignment with annotation\r
197          * file and sequence features file, and associate any tree-nodes. 2.\r
198          * connect alignment back to any associated dataset: 2.a. deuniquify\r
199          * recovers sequence information - but additionally, relocations must be\r
200          * made from the returned aligned sequence back to the dataset.\r
201          */\r
202         // construct annotated alignment as it would be done by the jalview\r
203         // applet\r
204         jalview.datamodel.Alignment al = new Alignment(alseqs);\r
205         // al.setDataset(dataset);\r
206         // make dataset\r
207         String inFile = null;\r
208         try\r
209         {\r
210           inFile = ((SeqSearchResult) result).getAnnotation();\r
211           if (inFile != null && inFile.length() > 0)\r
212           {\r
213             new jalview.io.AnnotationFile().readAnnotationFile(al, inFile,\r
214                     jalview.io.AppletFormatAdapter.PASTE);\r
215           }\r
216         } catch (Exception e)\r
217         {\r
218           System.err\r
219                   .println("Failed to parse the annotation file associated with the alignment.");\r
220           System.err.println(">>>EOF" + inFile + "\n<<<EOF\n");\r
221           e.printStackTrace(System.err);\r
222         }\r
223 \r
224         try\r
225         {\r
226           inFile = ((SeqSearchResult) result).getFeatures();\r
227           if (inFile != null && inFile.length() > 0)\r
228           {\r
229             jalview.io.FeaturesFile ff = new jalview.io.FeaturesFile(\r
230                     inFile, jalview.io.AppletFormatAdapter.PASTE);\r
231             ff.parse(al, featureColours, false);\r
232           }\r
233         } catch (Exception e)\r
234         {\r
235           System.err\r
236                   .println("Failed to parse the Features file associated with the alignment.");\r
237           System.err.println(">>>EOF" + inFile + "\n<<<EOF\n");\r
238           e.printStackTrace(System.err);\r
239         }\r
240         jalview.io.NewickFile nf = null;\r
241         try\r
242         {\r
243           inFile = ((SeqSearchResult) result).getNewickTree();\r
244           if (inFile != null && inFile.length() > 0)\r
245           {\r
246             nf = new jalview.io.NewickFile(inFile,\r
247                     jalview.io.AppletFormatAdapter.PASTE);\r
248             if (!nf.isValid())\r
249             {\r
250               nf.close();\r
251               nf = null;\r
252             }\r
253           }\r
254         } catch (Exception e)\r
255         {\r
256           System.err\r
257                   .println("Failed to parse the treeFile associated with the alignment.");\r
258           System.err.println(">>>EOF" + inFile + "\n<<<EOF\n");\r
259           e.printStackTrace(System.err);\r
260         }\r
261 \r
262         /*\r
263          * TODO: housekeeping w.r.t. recovery of dataset and annotation\r
264          * references for input sequences, and then dataset sequence creation\r
265          * for new sequences retrieved from service // finally, attempt to\r
266          * de-uniquify to recover input sequence identity, and try to map back\r
267          * onto dataset Note: this\r
268          * jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs, true); will\r
269          * NOT WORK - the returned alignment may contain multiple versions of\r
270          * the input sequence, each being a subsequence of the original.\r
271          * deuniquify also removes existing annotation and features added in the\r
272          * previous step... al.setDataset(dataset); // add in new sequences\r
273          * retrieved from sequence search which are not already in dataset. //\r
274          * trigger a 'fetchDBids' to annotate sequences with database ids...\r
275          */\r
276 \r
277         return new Object[]\r
278         { al, nf };\r
279       }\r
280       return null;\r
281     }\r
282 \r
283     /**\r
284      * mark subjob as cancelled and set result object appropriatly\r
285      */\r
286     void cancel()\r
287     {\r
288       cancelled = true;\r
289       subjobComplete = true;\r
290       result = null;\r
291     }\r
292 \r
293     /**\r
294      * \r
295      * @return boolean true if job can be submitted.\r
296      */\r
297     boolean hasValidInput()\r
298     {\r
299       if (seqs.getSeqs() != null)\r
300       {\r
301         return true;\r
302       }\r
303       return false;\r
304     }\r
305   }\r
306 \r
307   String alTitle; // name which will be used to form new alignment window.\r
308 \r
309   Alignment dataset; // dataset to which the new alignment will be\r
310 \r
311   // associated.\r
312 \r
313   ext.vamsas.SeqSearchI server = null;\r
314 \r
315   private String dbArg;\r
316 \r
317   /**\r
318    * set basic options for this (group) of Msa jobs\r
319    * \r
320    * @param subgaps\r
321    *                boolean\r
322    * @param presorder\r
323    *                boolean\r
324    */\r
325   SeqSearchWSThread(ext.vamsas.SeqSearchI server, String wsUrl,\r
326           WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,\r
327           AlignmentView alview, String wsname, String db)\r
328   {\r
329     super(alFrame, wsinfo, alview, wsname, wsUrl);\r
330     this.server = server;\r
331     this.dbArg = db;\r
332   }\r
333 \r
334   /**\r
335    * create one or more Msa jobs to align visible seuqences in _msa\r
336    * \r
337    * @param title\r
338    *                String\r
339    * @param _msa\r
340    *                AlignmentView\r
341    * @param subgaps\r
342    *                boolean\r
343    * @param presorder\r
344    *                boolean\r
345    * @param seqset\r
346    *                Alignment\r
347    */\r
348   SeqSearchWSThread(ext.vamsas.SeqSearchI server, String wsUrl,\r
349           WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,\r
350           String wsname, String title, AlignmentView _msa, String db,\r
351           Alignment seqset)\r
352   {\r
353     this(server, wsUrl, wsinfo, alFrame, _msa, wsname, db);\r
354     OutputHeader = wsInfo.getProgressText();\r
355     alTitle = title;\r
356     dataset = seqset;\r
357 \r
358     SequenceI[][] conmsa = _msa.getVisibleContigs('-');\r
359     if (conmsa != null)\r
360     {\r
361       int njobs = conmsa.length;\r
362       jobs = new SeqSearchWSJob[njobs];\r
363       for (int j = 0; j < njobs; j++)\r
364       {\r
365         if (j != 0)\r
366         {\r
367           jobs[j] = new SeqSearchWSJob(wsinfo.addJobPane(), conmsa[j]);\r
368         }\r
369         else\r
370         {\r
371           jobs[j] = new SeqSearchWSJob(0, conmsa[j]);\r
372         }\r
373         if (njobs > 0)\r
374         {\r
375           wsinfo\r
376                   .setProgressName("region " + jobs[j].jobnum,\r
377                           jobs[j].jobnum);\r
378         }\r
379         wsinfo.setProgressText(jobs[j].jobnum, OutputHeader);\r
380       }\r
381     }\r
382   }\r
383 \r
384   public boolean isCancellable()\r
385   {\r
386     return true;\r
387   }\r
388 \r
389   public void cancelJob()\r
390   {\r
391     if (!jobComplete && jobs != null)\r
392     {\r
393       boolean cancelled = true;\r
394       for (int job = 0; job < jobs.length; job++)\r
395       {\r
396         if (jobs[job].submitted && !jobs[job].subjobComplete)\r
397         {\r
398           String cancelledMessage = "";\r
399           try\r
400           {\r
401             vamsas.objects.simple.WsJobId cancelledJob = server\r
402                     .cancel(jobs[job].jobId);\r
403             if (cancelledJob.getStatus() == 2)\r
404             {\r
405               // CANCELLED_JOB\r
406               cancelledMessage = "Job cancelled.";\r
407               ((SeqSearchWSJob) jobs[job]).cancel();\r
408               wsInfo.setStatus(jobs[job].jobnum,\r
409                       WebserviceInfo.STATE_CANCELLED_OK);\r
410             }\r
411             else if (cancelledJob.getStatus() == 3)\r
412             {\r
413               // VALID UNSTOPPABLE JOB\r
414               cancelledMessage += "Server cannot cancel this job. just close the window.\n";\r
415               cancelled = false;\r
416               // wsInfo.setStatus(jobs[job].jobnum,\r
417               // WebserviceInfo.STATE_RUNNING);\r
418             }\r
419 \r
420             if (cancelledJob.getJobId() != null)\r
421             {\r
422               cancelledMessage += ("[" + cancelledJob.getJobId() + "]");\r
423             }\r
424 \r
425             cancelledMessage += "\n";\r
426           } catch (Exception exc)\r
427           {\r
428             cancelledMessage += ("\nProblems cancelling the job : Exception received...\n"\r
429                     + exc + "\n");\r
430             Cache.log.warn(\r
431                     "Exception whilst cancelling " + jobs[job].jobId, exc);\r
432           }\r
433           wsInfo.setProgressText(jobs[job].jobnum, OutputHeader\r
434                   + cancelledMessage + "\n");\r
435         }\r
436       }\r
437       if (cancelled)\r
438       {\r
439         wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);\r
440         jobComplete = true;\r
441       }\r
442       this.interrupt(); // kick thread to update job states.\r
443     }\r
444     else\r
445     {\r
446       if (!jobComplete)\r
447       {\r
448         wsInfo\r
449                 .setProgressText(OutputHeader\r
450                         + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");\r
451       }\r
452     }\r
453   }\r
454 \r
455   void pollJob(WSJob job) throws Exception\r
456   {\r
457     ((SeqSearchWSJob) job).result = server\r
458             .getResult(((SeqSearchWSJob) job).jobId);\r
459   }\r
460 \r
461   void StartJob(WSJob job)\r
462   {\r
463     if (!(job instanceof SeqSearchWSJob))\r
464     {\r
465       throw new Error("StartJob(MsaWSJob) called on a WSJobInstance "\r
466               + job.getClass());\r
467     }\r
468     SeqSearchWSJob j = (SeqSearchWSJob) job;\r
469     if (j.submitted)\r
470     {\r
471       if (Cache.log.isDebugEnabled())\r
472       {\r
473         Cache.log.debug("Tried to submit an already submitted job "\r
474                 + j.jobId);\r
475       }\r
476       return;\r
477     }\r
478     if (j.seqs.getSeqs() == null)\r
479     {\r
480       // special case - selection consisted entirely of empty sequences...\r
481       j.submitted = true;\r
482       j.result = new MsaResult();\r
483       j.result.setFinished(true);\r
484       j.result.setStatus("Empty Alignment Job");\r
485       ((MsaResult) j.result).setMsa(null);\r
486     }\r
487     try\r
488     {\r
489       vamsas.objects.simple.WsJobId jobsubmit = server.search(j.seqs\r
490               .getSeqs()[0], dbArg);\r
491 \r
492       if ((jobsubmit != null) && (jobsubmit.getStatus() == 1))\r
493       {\r
494         j.jobId = jobsubmit.getJobId();\r
495         j.submitted = true;\r
496         j.subjobComplete = false;\r
497         // System.out.println(WsURL + " Job Id '" + jobId + "'");\r
498       }\r
499       else\r
500       {\r
501         if (jobsubmit == null)\r
502         {\r
503           throw new Exception(\r
504                   "Server at "\r
505                           + WsUrl\r
506                           + " returned null object, it probably cannot be contacted. Try again later ?");\r
507         }\r
508 \r
509         throw new Exception(jobsubmit.getJobId());\r
510       }\r
511     } catch (Exception e)\r
512     {\r
513       // TODO: JBPNote catch timeout or other fault types explicitly\r
514       // For unexpected errors\r
515       System.err\r
516               .println(WebServiceName\r
517                       + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"\r
518                       + "When contacting Server:" + WsUrl + "\n"\r
519                       + e.toString() + "\n");\r
520       j.allowedServerExceptions = 0;\r
521       wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);\r
522       wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_SERVERERROR);\r
523       wsInfo\r
524               .appendProgressText(\r
525                       j.jobnum,\r
526                       "Failed to submit sequences for alignment.\n"\r
527                               + "It is most likely that there is a problem with the server.\n"\r
528                               + "Just close the window\n");\r
529 \r
530       // e.printStackTrace(); // TODO: JBPNote DEBUG\r
531     }\r
532   }\r
533 \r
534   private jalview.datamodel.Sequence[] getVamsasAlignment(\r
535           vamsas.objects.simple.Alignment valign)\r
536   {\r
537     vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs();\r
538     jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs.length];\r
539 \r
540     for (int i = 0, j = seqs.length; i < j; i++)\r
541     {\r
542       msa[i] = new jalview.datamodel.Sequence(seqs[i].getId(), seqs[i]\r
543               .getSeq());\r
544     }\r
545 \r
546     return msa;\r
547   }\r
548 \r
549   void parseResult()\r
550   {\r
551     int results = 0; // number of result sets received\r
552     JobStateSummary finalState = new JobStateSummary();\r
553     try\r
554     {\r
555       for (int j = 0; j < jobs.length; j++)\r
556       {\r
557         finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);\r
558         if (jobs[j].submitted && jobs[j].subjobComplete\r
559                 && jobs[j].hasResults())\r
560         {\r
561           results++;\r
562           vamsas.objects.simple.Alignment valign = ((SeqSearchResult) jobs[j].result)\r
563                   .getAlignment();\r
564           if (valign != null)\r
565           {\r
566             wsInfo.appendProgressText(jobs[j].jobnum,\r
567                     "\nAlignment Object Method Notes\n");\r
568             String[] lines = valign.getMethod();\r
569             for (int line = 0; line < lines.length; line++)\r
570             {\r
571               wsInfo.appendProgressText(jobs[j].jobnum, lines[line] + "\n");\r
572             }\r
573             // JBPNote The returned files from a webservice could be\r
574             // hidden behind icons in the monitor window that,\r
575             // when clicked, pop up their corresponding data\r
576           }\r
577         }\r
578       }\r
579     } catch (Exception ex)\r
580     {\r
581 \r
582       Cache.log.error("Unexpected exception when processing results for "\r
583               + alTitle, ex);\r
584       wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);\r
585     }\r
586     if (results > 0)\r
587     {\r
588       wsInfo.showResultsNewFrame\r
589               .addActionListener(new java.awt.event.ActionListener()\r
590               {\r
591                 public void actionPerformed(java.awt.event.ActionEvent evt)\r
592                 {\r
593                   displayResults(true);\r
594                 }\r
595               });\r
596       wsInfo.mergeResults\r
597               .addActionListener(new java.awt.event.ActionListener()\r
598               {\r
599                 public void actionPerformed(java.awt.event.ActionEvent evt)\r
600                 {\r
601                   displayResults(false);\r
602                 }\r
603               });\r
604       wsInfo.setResultsReady();\r
605     }\r
606     else\r
607     {\r
608       wsInfo.setFinishedNoResults();\r
609     }\r
610   }\r
611 \r
612   void displayResults(boolean newFrame)\r
613   {\r
614     if (!newFrame)\r
615     {\r
616       System.err.println("MERGE WITH OLD FRAME NOT IMPLEMENTED");\r
617       return;\r
618     }\r
619     // each subjob is an independent alignment for the moment\r
620     // Alignment al[] = new Alignment[jobs.length];\r
621     // NewickFile nf[] = new NewickFile[jobs.length];\r
622     for (int j = 0; j < jobs.length; j++)\r
623     {\r
624       Hashtable featureColours = new Hashtable();\r
625       Alignment al = null;\r
626       NewickFile nf = null;\r
627       if (jobs[j].hasResults())\r
628       {\r
629         Object[] res = ((SeqSearchWSJob) jobs[j]).getAlignment(dataset,\r
630                 featureColours);\r
631         if (res == null)\r
632         {\r
633           continue;\r
634         }\r
635         ;\r
636         al = (Alignment) res[0];\r
637         nf = (NewickFile) res[1];\r
638       }\r
639       else\r
640       {\r
641         al = null;\r
642         nf = null;\r
643         continue;\r
644       }\r
645       /*\r
646        * We can't map new alignment back with insertions from input's hidden\r
647        * regions until dataset mapping is sorted out... but basically it goes\r
648        * like this: 1. Merge each domain hit back onto the visible segments in\r
649        * the same way as a Jnet prediction is mapped back\r
650        * \r
651        * Object[] newview = input.getUpdatedView(results, orders, getGapChar()); //\r
652        * trash references to original result data for (int j = 0; j <\r
653        * jobs.length; j++) { results[j] = null; orders[j] = null; } SequenceI[]\r
654        * alignment = (SequenceI[]) newview[0]; ColumnSelection columnselection =\r
655        * (ColumnSelection) newview[1]; Alignment al = new Alignment(alignment);\r
656        * \r
657        * if (dataset != null) { al.setDataset(dataset); }\r
658        * \r
659        * propagateDatasetMappings(al); }\r
660        */\r
661 \r
662       AlignFrame af = new AlignFrame(al,// columnselection,\r
663               AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);\r
664       if (nf != null)\r
665       {\r
666         af.ShowNewickTree(nf, "Tree from " + this.alTitle);\r
667       }\r
668       // initialise with same renderer settings as in parent alignframe.\r
669       af.getFeatureRenderer().transferSettings(this.featureSettings);\r
670       Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,\r
671               AlignFrame.DEFAULT_HEIGHT);\r
672     }\r
673   }\r
674 \r
675   public boolean canMergeResults()\r
676   {\r
677     return false;\r
678   }\r
679 }\r