moved visible sub-alignment extraction and update from MsaWSThread to
[jalview.git] / src / jalview / ws / JPredClient.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer\r
3  * Copyright (C) 2005 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 javax.swing.*;\r
24 \r
25 import ext.vamsas.*;\r
26 import jalview.analysis.*;\r
27 import jalview.bin.*;\r
28 import jalview.datamodel.*;\r
29 import jalview.datamodel.Alignment;\r
30 import jalview.datamodel.AlignmentView;\r
31 import jalview.gui.*;\r
32 import jalview.io.*;\r
33 import jalview.util.*;\r
34 import jalview.ws.WSThread.*;\r
35 import vamsas.objects.simple.*;\r
36 \r
37 public class JPredClient\r
38     extends WSClient\r
39 {\r
40   AlignFrame parentFrame=null;\r
41     /**\r
42      * crate a new GUI JPred Job\r
43      * @param sh ServiceHandle\r
44      * @param title String\r
45      * @param msa boolean - true - submit alignment as a sequence profile\r
46      * @param alview AlignmentView\r
47      */\r
48     public JPredClient(ext.vamsas.ServiceHandle sh, String title, boolean msa, AlignmentView alview, AlignFrame parentFrame) {\r
49     wsInfo=setWebService(sh);\r
50     this.parentFrame=parentFrame;\r
51     startJPredClient(title, msa, alview);\r
52 \r
53   }\r
54 \r
55   /**\r
56    * startJPredClient\r
57    *\r
58    * @param title String\r
59    * @param msa boolean\r
60    * @param alview AlignmentView\r
61    */\r
62   private void startJPredClient(String title, boolean msa,\r
63                                 jalview.datamodel.AlignmentView alview)\r
64   {\r
65   }\r
66 \r
67   public JPredClient(ext.vamsas.ServiceHandle sh, String title, SequenceI seq, AlignFrame parentFrame)\r
68   {\r
69     wsInfo = setWebService(sh);\r
70     this.parentFrame=parentFrame;\r
71     startJPredClient(title, seq);\r
72   }\r
73 \r
74   public JPredClient(ext.vamsas.ServiceHandle sh, String title, SequenceI[] msa, AlignFrame parentFrame)\r
75   {\r
76     wsInfo = setWebService(sh);\r
77     this.parentFrame=parentFrame;\r
78     startJPredClient(title, msa);\r
79   }\r
80 \r
81   public JPredClient(String title, SequenceI[] msf)\r
82   {\r
83     startJPredClient(title, msf);\r
84   }\r
85 \r
86   public JPredClient(String title, SequenceI seq)\r
87   {\r
88     startJPredClient(title, seq);\r
89   }\r
90 \r
91   private void startJPredClient(String title, SequenceI[] msf)\r
92   {\r
93     if (wsInfo == null)\r
94     {\r
95       wsInfo = setWebService();\r
96     }\r
97 \r
98     SequenceI seq = msf[0];\r
99 \r
100     String altitle = "JNet prediction on " + seq.getName() +\r
101         " using alignment from " + title;\r
102 \r
103     wsInfo.setProgressText("Job details for MSA based prediction (" +\r
104                            title + ") on sequence :\n>" + seq.getName() + "\n" +\r
105                            AlignSeq.extractGaps("-. ", seq.getSequence()) +\r
106                            "\n");\r
107     SequenceI aln[] = new SequenceI[msf.length];\r
108     for (int i = 0, j = msf.length; i < j; i++)\r
109     {\r
110       aln[i] = new jalview.datamodel.Sequence(msf[i]);\r
111     }\r
112 \r
113     Hashtable SequenceInfo = jalview.analysis.SeqsetUtils.uniquify(aln, true);\r
114 \r
115     Jpred server = locateWebService();\r
116     if (server==null)\r
117     {\r
118       return;\r
119     }\r
120 \r
121     JPredThread jthread = new JPredThread(wsInfo, altitle, server, SequenceInfo, aln);\r
122     wsInfo.setthisService(jthread);\r
123     jthread.start();\r
124   }\r
125 \r
126   public void startJPredClient(String title, SequenceI seq)\r
127   {\r
128     if (wsInfo == null)\r
129     {\r
130       wsInfo = setWebService();\r
131     }\r
132     wsInfo.setProgressText("Job details for prediction on sequence :\n>" +\r
133                            seq.getName() + "\n" +\r
134                            AlignSeq.extractGaps("-. ", seq.getSequence()) +\r
135                            "\n");\r
136     String altitle = "JNet prediction for sequence " + seq.getName() + " from " +\r
137         title;\r
138 \r
139     Hashtable SequenceInfo = jalview.analysis.SeqsetUtils.SeqCharacterHash(seq);\r
140 \r
141     Jpred server = locateWebService();\r
142     if (server==null)\r
143     {\r
144       return;\r
145     }\r
146 \r
147     JPredThread jthread = new JPredThread(wsInfo, altitle, server, SequenceInfo, seq);\r
148     wsInfo.setthisService(jthread);\r
149     jthread.start();\r
150   }\r
151 \r
152   private WebserviceInfo setWebService()\r
153   {\r
154     WebServiceName = "JNetWS";\r
155     WebServiceJobTitle = "JNet secondary structure prediction";\r
156     WebServiceReference =\r
157         "\"Cuff J. A and Barton G.J (2000) Application of " +\r
158         "multiple sequence alignment profiles to improve protein secondary structure prediction, " +\r
159         "Proteins 40:502-511\".";\r
160     WsURL = "http://www.compbio.dundee.ac.uk/JalviewWS/services/jpred";\r
161 \r
162     WebserviceInfo wsInfo = new WebserviceInfo(WebServiceJobTitle,\r
163                                                WebServiceReference);\r
164 \r
165     return wsInfo;\r
166   }\r
167 \r
168   private ext.vamsas.Jpred locateWebService()\r
169   {\r
170     ext.vamsas.JpredServiceLocator loc = new JpredServiceLocator(); // Default\r
171     ext.vamsas.Jpred server=null;\r
172     try\r
173     {\r
174       server = loc.getjpred(new java.net.URL(WsURL)); // JBPNote will be set from properties\r
175       ( (JpredSoapBindingStub)server).setTimeout(60000); // one minute stub\r
176       //((JpredSoapBindingStub)this.server)._setProperty(org.apache.axis.encoding.C, Boolean.TRUE);\r
177 \r
178     }\r
179     catch (Exception ex)\r
180     {\r
181       JOptionPane.showMessageDialog(Desktop.desktop,\r
182                                     "The Secondary Structure Prediction Service named " +\r
183                                     WebServiceName + " at " + WsURL +\r
184                                     " couldn't be located.",\r
185                                     "Internal Jalview Error",\r
186                                     JOptionPane.WARNING_MESSAGE);\r
187       wsInfo.setProgressText("Serious! " + WebServiceName +\r
188                              " Service location failed\nfor URL :" + WsURL +\r
189                              "\n" +\r
190                              ex.getMessage());\r
191       wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);\r
192 \r
193     }\r
194 \r
195     return server;\r
196   }\r
197 \r
198   class JPredThread\r
199       extends WSThread\r
200       implements WSClientI\r
201   {\r
202     class JPredJob\r
203         extends WSThread.WSJob\r
204     {\r
205 \r
206       vamsas.objects.simple.Sequence sequence;\r
207       vamsas.objects.simple.Msfalignment msa;\r
208       java.util.Hashtable SequenceInfo = null;\r
209       /**\r
210        *\r
211        * @return true if getResultSet will return a valid alignment and prediction result.\r
212        */\r
213       public boolean hasResults()\r
214       {\r
215         if (subjobComplete && result != null && result.isFinished()\r
216             && ( (JpredResult) result).getPredfile() != null &&\r
217             ( (JpredResult) result).getAligfile() != null)\r
218         {\r
219           return true;\r
220         }\r
221         return false;\r
222       }\r
223 \r
224       boolean hasValidInput()\r
225       {\r
226         if (sequence != null)\r
227         {\r
228           return true;\r
229         }\r
230         return false;\r
231       }\r
232 \r
233       public Alignment getResultSet()\r
234           throws Exception\r
235       {\r
236         if (result == null || !result.isFinished())\r
237         {\r
238           return null;\r
239         }\r
240         Alignment al = null;\r
241         int FirstSeq = -1; // the position of the query sequence in Alignment al\r
242         boolean noMsa = true; // set if no MSA has been returned by JPred\r
243 \r
244         JpredResult result = (JpredResult)this.result;\r
245 \r
246         jalview.bin.Cache.log.debug("Parsing output from JNet job.");\r
247         // JPredFile prediction = new JPredFile("C:/JalviewX/files/jpred.txt", "File");\r
248         jalview.io.JPredFile prediction = new jalview.io.JPredFile(result.\r
249             getPredfile(),\r
250             "Paste");\r
251         SequenceI[] preds = prediction.getSeqsAsArray();\r
252         jalview.bin.Cache.log.debug("Got prediction profile.");\r
253 \r
254         if ( (this.msa != null) && (result.getAligfile() != null))\r
255         {\r
256           jalview.bin.Cache.log.debug("Getting associated alignment.");\r
257           // we ignore the returned alignment if we only predicted on a single sequence\r
258           String format = new jalview.io.IdentifyFile().Identify(result.\r
259               getAligfile(),\r
260               "Paste");\r
261 \r
262           if (jalview.io.FormatAdapter.isValidFormat(format))\r
263           {\r
264             al = new Alignment(new FormatAdapter().readFile(result.getAligfile(),\r
265                 "Paste", format));\r
266             SequenceI sqs[] = new SequenceI[al.getHeight()];\r
267             for (int i = 0, j = al.getHeight(); i < j; i++)\r
268             {\r
269               sqs[i] = al.getSequenceAt(i);\r
270             }\r
271             if (!jalview.analysis.SeqsetUtils.deuniquify( (Hashtable)\r
272                 SequenceInfo, sqs))\r
273             {\r
274               throw (new Exception(\r
275                   "Couldn't recover sequence properties for alignment."));\r
276             }\r
277 \r
278             noMsa = false;\r
279             FirstSeq = 0;\r
280           }\r
281           else\r
282           {\r
283             throw (new Exception(\r
284                 "Unknown format "+format+" for file : \n" +\r
285                 result.getAligfile()));\r
286           }\r
287         }\r
288         else\r
289         {\r
290           al = new Alignment(preds);\r
291           FirstSeq = prediction.getQuerySeqPosition();\r
292           if (!jalview.analysis.SeqsetUtils.SeqCharacterUnhash(\r
293               al.getSequenceAt(FirstSeq), SequenceInfo))\r
294           {\r
295             throw (new Exception(\r
296                 "Couldn't recover sequence properties for JNet Query sequence!"));\r
297           }\r
298         }\r
299 \r
300         al.setDataset(null);\r
301 \r
302         jalview.io.JnetAnnotationMaker.add_annotation(prediction, al, FirstSeq,\r
303             noMsa);\r
304         return al; // , FirstSeq, noMsa};\r
305       }\r
306       public JPredJob(Hashtable SequenceInfo, SequenceI seq)\r
307       {\r
308         super();\r
309         String sq = AlignSeq.extractGaps(Comparison.GapChars, seq.getSequence());\r
310         if (sq.length() >= 20)\r
311         {\r
312           this.SequenceInfo = SequenceInfo;\r
313           sequence = new vamsas.objects.simple.Sequence();\r
314           sequence.setId(seq.getName());\r
315           sequence.setSeq(sq);\r
316         }\r
317       }\r
318 \r
319       public JPredJob(Hashtable SequenceInfo, SequenceI[] msf)\r
320       {\r
321         this(SequenceInfo, msf[0]);\r
322         if (sequence != null)\r
323         {\r
324           if (msf.length > 1)\r
325           {\r
326             msa = new vamsas.objects.simple.Msfalignment();\r
327             jalview.io.PileUpfile pileup = new jalview.io.PileUpfile();\r
328             msa.setMsf(pileup.print(msf));\r
329           }\r
330         }\r
331       }\r
332     }\r
333     ext.vamsas.Jpred server;\r
334     String altitle = "";\r
335     JPredThread(WebserviceInfo wsinfo, String altitle, ext.vamsas.Jpred server) {\r
336       this.altitle = altitle;\r
337       this.server = server;\r
338       this.wsInfo = wsinfo;\r
339     }\r
340 \r
341 //    String OutputHeader;\r
342 //    vamsas.objects.simple.JpredResult result;\r
343 \r
344     JPredThread(WebserviceInfo wsinfo, String altitle, ext.vamsas.Jpred server, Hashtable SequenceInfo,SequenceI seq)\r
345     {\r
346       this(wsinfo, altitle, server);\r
347       JPredJob job = new JPredJob(SequenceInfo, seq);\r
348       if (job.hasValidInput())\r
349       {\r
350         OutputHeader = wsInfo.getProgressText();\r
351         jobs = new WSJob[]\r
352             {\r
353             job};\r
354         job.jobnum = 0;\r
355       }\r
356     }\r
357 \r
358     JPredThread(WebserviceInfo wsinfo, String altitle, ext.vamsas.Jpred server, Hashtable SequenceInfo, SequenceI[] msf)\r
359     {\r
360       this(wsinfo, altitle, server);\r
361       JPredJob job = new JPredJob(SequenceInfo, msf);\r
362       if (job.hasValidInput())\r
363       {\r
364         jobs = new WSJob[]\r
365             {\r
366             job};\r
367         OutputHeader = wsInfo.getProgressText();\r
368         job.jobnum = 0;\r
369       }\r
370     }\r
371 \r
372     /*\r
373         public void run()\r
374         {\r
375           StartJob();\r
376 \r
377           while (!jobComplete && (allowedServerExceptions > 0))\r
378           {\r
379             try\r
380             {\r
381               if ( (result = server.getresult(jobId)) == null)\r
382               {\r
383                 throw (new Exception(\r
384      "Timed out when communicating with server\nTry again later.\n"));\r
385               }\r
386               if (result.getState()==0)\r
387                 jalview.bin.Cache.log.debug("Finished "+jobId);\r
388               if (result.isRunning())\r
389               {\r
390                 wsInfo.setStatus(WebserviceInfo.STATE_RUNNING);\r
391               }\r
392               if (result.isQueued())\r
393               {\r
394                 wsInfo.setStatus(WebserviceInfo.STATE_QUEUING);\r
395               }\r
396 \r
397               wsInfo.setProgressText(OutputHeader + "\n" +\r
398                                      result.getStatus());\r
399 \r
400               if (result.isFinished())\r
401               {\r
402 \r
403                 parseResult();\r
404                 jobComplete = true;\r
405                 jobsRunning--;\r
406               } else {\r
407                 // catch exceptions\r
408                 if (! (result.isJobFailed() || result.isServerError()))\r
409                 {\r
410                   try\r
411                   {\r
412                     Thread.sleep(5000);\r
413                   }\r
414                   catch (InterruptedException ex1)\r
415                   {\r
416                   }\r
417 \r
418                   //  System.out.println("I'm alive "+seqid+" "+jobid);\r
419                 }\r
420                 else\r
421                 {\r
422                   wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);\r
423                   jobsRunning--;\r
424                   jobComplete = true;\r
425                 }\r
426               }\r
427             }\r
428             catch (Exception ex)\r
429             {\r
430               allowedServerExceptions--;\r
431 \r
432               wsInfo.appendProgressText("\nJPredWS Server exception!\n" +\r
433                                         ex.getMessage());\r
434 \r
435               try\r
436               {\r
437                 if (allowedServerExceptions > 0)\r
438                 {\r
439                   Thread.sleep(5000);\r
440                 }\r
441               }\r
442               catch (InterruptedException ex1)\r
443               {\r
444               }\r
445             }\r
446             catch (OutOfMemoryError er)\r
447             {\r
448               jobComplete = true;\r
449               wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);\r
450               JOptionPane.showInternalMessageDialog(Desktop.desktop,\r
451      "Out of memory handling result!!"\r
452                                                     +\r
453      "\nSee help files for increasing Java Virtual Machine memory."\r
454                                                     , "Out of memory",\r
455      JOptionPane.WARNING_MESSAGE);\r
456               System.out.println("JPredClient: "+er);\r
457               System.gc();\r
458             }\r
459           }\r
460           if (result!=null)\r
461             if (! (result.isJobFailed() || result.isServerError()))\r
462             {\r
463               wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_OK);\r
464             }\r
465             else\r
466             {\r
467               wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);\r
468             }\r
469         }\r
470      */\r
471     void StartJob(WSJob j)\r
472     {\r
473       if (! (j instanceof JPredJob))\r
474       {\r
475         throw new Error("Implementation error - StartJob(JpredJob) called on " +\r
476                         j.getClass());\r
477       }\r
478       try\r
479       {\r
480         JPredJob job = (JPredJob) j;\r
481         if (job.msa != null)\r
482         {\r
483           job.jobId = server.predictOnMsa(job.msa);\r
484         }\r
485         else\r
486           if (job.sequence!=null)\r
487           {\r
488             job.jobId = server.predict(job.sequence);\r
489           }\r
490 \r
491         if (job.jobId != null)\r
492         {\r
493           if (job.jobId.startsWith("Broken"))\r
494           {\r
495             job.result = (vamsas.objects.simple.Result)new JpredResult();\r
496             job.result.setInvalid(true);\r
497             job.result.setStatus("Submission " + job.jobId);\r
498           }\r
499           else\r
500           {\r
501             job.submitted = true;\r
502             job.subjobComplete = false;\r
503             Cache.log.info(WsURL + " Job Id '" + job.jobId + "'");\r
504           }\r
505         }\r
506         else\r
507         {\r
508           throw new Exception("Server timed out - try again later\n");\r
509         }\r
510       }\r
511       catch (Exception e)\r
512       {\r
513         if (e.getMessage().indexOf("Exception") > -1)\r
514         {\r
515           wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_SERVERERROR);\r
516           wsInfo.setProgressText(j.jobnum,\r
517                                  "Failed to submit the prediction. (Just close the window)\n"\r
518                                  +\r
519                                  "It is most likely that there is a problem with the server.\n");\r
520           System.err.println(\r
521               "JPredWS Client: Failed to submit the prediction. Quite possibly because of a server error - see below)\n" +\r
522               e.getMessage() + "\n");\r
523 \r
524           jalview.bin.Cache.log.warn("Server Exception", e);\r
525         }\r
526         else\r
527         {\r
528           wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR);\r
529           // JBPNote - this could be a popup informing the user of the problem.\r
530           wsInfo.appendProgressText(j.jobnum,\r
531                                     "Failed to submit the prediction:\n"\r
532                                     + e.getMessage() +\r
533                                     wsInfo.getProgressText());\r
534 \r
535           jalview.bin.Cache.log.debug("Failed Submission of job " + j.jobnum, e);\r
536 \r
537         }\r
538         j.allowedServerExceptions = -1;\r
539         j.subjobComplete = true;\r
540       }\r
541     }\r
542 \r
543     /*  private void addFloatAnnotations(Alignment al, int[] gapmap,\r
544                                        Vector values, String Symname,\r
545                                        String Visname, float min,\r
546                                        float max, int winLength)\r
547       {\r
548         Annotation[] annotations = new Annotation[al.getWidth()];\r
549 \r
550         for (int j = 0; j < values.size(); j++)\r
551         {\r
552           float value = Float.parseFloat(values.get(j).toString());\r
553           annotations[gapmap[j]] = new Annotation("", value + "", ' ',\r
554                                                   value);\r
555         }\r
556 \r
557         al.addAnnotation(new AlignmentAnnotation(Symname, Visname,\r
558      annotations, min, max, winLength));\r
559       }*/\r
560 \r
561     void parseResult()\r
562     {\r
563       int results = 0; // number of result sets received\r
564       JobStateSummary finalState = new JobStateSummary();\r
565       try\r
566       {\r
567         for (int j = 0; j < jobs.length; j++)\r
568         {\r
569           finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);\r
570           if (jobs[j].submitted && jobs[j].subjobComplete && jobs[j].hasResults())\r
571           {\r
572             results++;\r
573           }\r
574         }\r
575       }\r
576       catch (Exception ex)\r
577       {\r
578 \r
579         Cache.log.error("Unexpected exception when processing results for " +\r
580                         altitle, ex);\r
581         wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);\r
582       }\r
583       if (results > 0)\r
584       {\r
585         wsInfo.showResultsNewFrame\r
586             .addActionListener(new java.awt.event.ActionListener()\r
587         {\r
588           public void actionPerformed(\r
589               java.awt.event.ActionEvent evt)\r
590           {\r
591             displayResults(true);\r
592           }\r
593         });\r
594         wsInfo.mergeResults\r
595             .addActionListener(new java.awt.event.ActionListener()\r
596         {\r
597           public void actionPerformed(\r
598               java.awt.event.ActionEvent evt)\r
599           {\r
600             displayResults(false);\r
601           }\r
602         });\r
603         wsInfo.setResultsReady();\r
604       }\r
605       else\r
606       {\r
607         wsInfo.setFinishedNoResults();\r
608       }\r
609     }\r
610 \r
611     void displayResults(boolean newWindow)\r
612     {\r
613       if (jobs != null)\r
614       {\r
615         Alignment res = null;\r
616         for (int jn = 0; jn < jobs.length; jn++)\r
617         {\r
618           Alignment jobres = null;\r
619           JPredJob j = (JPredJob) jobs[jn];\r
620 \r
621           if (j.hasResults())\r
622           {\r
623             try\r
624             {\r
625               jalview.bin.Cache.log.debug("Parsing output of job " + jn);\r
626               jobres = j.getResultSet();\r
627               jalview.bin.Cache.log.debug("Finished parsing output.");\r
628               if (jobs.length==1)\r
629                 res = jobres;\r
630               else {\r
631                   // do merge with other job results\r
632               }\r
633             }\r
634             catch (Exception e)\r
635             {\r
636               jalview.bin.Cache.log.error(\r
637                   "JNet Client: JPred Annotation Parse Error",\r
638                   e);\r
639               wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR);\r
640               wsInfo.appendProgressText(j.jobnum,\r
641                                         OutputHeader + "\n" +\r
642                                         j.result.getStatus() +\r
643                                         "\nInvalid JNet job result data!\n" +\r
644                                         e.getMessage());\r
645               j.result.setBroken(true);\r
646             }\r
647           }\r
648         }\r
649 \r
650         if (res != null)\r
651         {\r
652           if (newWindow)\r
653           {\r
654             AlignFrame af = new AlignFrame(res);\r
655             Desktop.addInternalFrame(af, altitle,\r
656                                      AlignFrame.NEW_WINDOW_WIDTH,\r
657                                      AlignFrame.NEW_WINDOW_HEIGHT);\r
658           }\r
659           else\r
660           {\r
661             Cache.log.info("Append results onto existing alignment.");\r
662           }\r
663         }\r
664       }\r
665     }\r
666     void pollJob(WSJob job)\r
667         throws Exception\r
668     {\r
669       job.result = server.getresult(job.jobId);\r
670     }\r
671     public boolean isCancellable()\r
672     {\r
673       return false;\r
674     }\r
675 \r
676     public void cancelJob()\r
677     {\r
678       throw new Error("Implementation error!");\r
679     }\r
680 \r
681     public boolean canMergeResults()\r
682     {\r
683       return false;\r
684     }\r
685 \r
686   }\r
687 }\r
688 \r