Merge branch 'groovy/JAL-2074_upgradeto2.4.4' into develop
[jalview.git] / src / jalview / bin / Jalview.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.bin;
22
23 import jalview.gui.AlignFrame;
24 import jalview.gui.Desktop;
25 import jalview.io.BioJsHTMLOutput;
26 import jalview.io.HtmlSvgOutput;
27 import jalview.util.MessageManager;
28 import jalview.util.Platform;
29 import jalview.ws.jws2.Jws2Discoverer;
30
31 import java.awt.event.ActionEvent;
32 import java.awt.event.ActionListener;
33 import java.io.BufferedReader;
34 import java.io.File;
35 import java.io.FileOutputStream;
36 import java.io.IOException;
37 import java.io.OutputStreamWriter;
38 import java.io.PrintWriter;
39 import java.lang.reflect.Constructor;
40 import java.net.MalformedURLException;
41 import java.net.URI;
42 import java.net.URL;
43 import java.net.URLDecoder;
44 import java.security.AllPermission;
45 import java.security.CodeSource;
46 import java.security.PermissionCollection;
47 import java.security.Permissions;
48 import java.security.Policy;
49 import java.util.HashMap;
50 import java.util.Map;
51 import java.util.Vector;
52
53 import javax.swing.UIManager;
54
55 /**
56  * Main class for Jalview Application <br>
57  * <br>
58  * start with java -Djava.ext.dirs=$PATH_TO_LIB$ jalview.bin.Jalview
59  * 
60  * @author $author$
61  * @version $Revision$
62  */
63 public class Jalview
64 {
65   static
66   {
67     // grab all the rights we can the JVM
68     Policy.setPolicy(new Policy()
69     {
70       @Override
71       public PermissionCollection getPermissions(CodeSource codesource)
72       {
73         Permissions perms = new Permissions();
74         perms.add(new AllPermission());
75         return (perms);
76       }
77
78       @Override
79       public void refresh()
80       {
81       }
82     });
83   }
84
85   /**
86    * main class for Jalview application
87    * 
88    * @param args
89    *          open <em>filename</em>
90    */
91   public static void main(String[] args)
92   {
93     System.setSecurityManager(null);
94     System.out.println("Java version: "
95             + System.getProperty("java.version"));
96     System.out.println(System.getProperty("os.arch") + " "
97             + System.getProperty("os.name") + " "
98             + System.getProperty("os.version"));
99
100     ArgsParser aparser = new ArgsParser(args);
101     boolean headless = false;
102
103     if (aparser.contains("help") || aparser.contains("h"))
104     {
105       showUsage();
106       System.exit(0);
107     }
108     if (aparser.contains("nodisplay") || aparser.contains("nogui")
109             || aparser.contains("headless"))
110     {
111       System.setProperty("java.awt.headless", "true");
112       headless = true;
113     }
114     String usrPropsFile = aparser.getValue("props");
115     Cache.loadProperties(usrPropsFile); // must do this before
116     if (usrPropsFile != null)
117     {
118       System.out.println("CMD [-props " + usrPropsFile
119               + "] executed successfully!");
120     }
121
122     // anything else!
123
124     final String jabawsUrl = aparser.getValue("jabaws");
125     if (jabawsUrl != null)
126     {
127       try
128       {
129         Jws2Discoverer.getDiscoverer().setPreferredUrl(jabawsUrl);
130         System.out.println("CMD [-jabaws " + jabawsUrl
131                 + "] executed successfully!");
132       } catch (MalformedURLException e)
133       {
134         System.err.println("Invalid jabaws parameter: " + jabawsUrl
135                 + " ignored");
136       }
137     }
138
139     String defs = aparser.getValue("setprop");
140     while (defs != null)
141     {
142       int p = defs.indexOf('=');
143       if (p == -1)
144       {
145         System.err.println("Ignoring invalid setprop argument : " + defs);
146       }
147       else
148       {
149         System.out.println("Executing setprop argument: " + defs);
150         // DISABLED FOR SECURITY REASONS
151         // TODO: add a property to allow properties to be overriden by cli args
152         // Cache.setProperty(defs.substring(0,p), defs.substring(p+1));
153       }
154       defs = aparser.getValue("setprop");
155     }
156     if (System.getProperty("java.awt.headless") != null
157             && System.getProperty("java.awt.headless").equals("true"))
158     {
159       headless = true;
160     }
161     System.setProperty("http.agent",
162             "Jalview Desktop/" + Cache.getDefault("VERSION", "Unknown"));
163     try
164     {
165       Cache.initLogger();
166     } catch (java.lang.NoClassDefFoundError error)
167     {
168       error.printStackTrace();
169       System.out
170               .println("\nEssential logging libraries not found."
171                       + "\nUse: java -Djava.ext.dirs=$PATH_TO_LIB$ jalview.bin.Jalview");
172       System.exit(0);
173     }
174
175     Desktop desktop = null;
176
177     try
178     {
179       UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
180     } catch (Exception ex)
181     {
182     }
183     if (Platform.isAMac())
184     {
185       System.setProperty("com.apple.mrj.application.apple.menu.about.name",
186               "Jalview");
187       System.setProperty("apple.laf.useScreenMenuBar", "true");
188       try
189       {
190         UIManager.setLookAndFeel(ch.randelshofer.quaqua.QuaquaManager
191                 .getLookAndFeel());
192       } catch (Throwable e)
193       {
194         System.err.println("Failed to set QuaQua look and feel: "
195                 + e.toString());
196       }
197     }
198
199     if (!headless)
200     {
201       desktop = new Desktop();
202       desktop.setInBatchMode(true); // indicate we are starting up
203       desktop.setVisible(true);
204       desktop.startServiceDiscovery();
205       if (!aparser.contains("nousagestats"))
206       {
207         startUsageStats(desktop);
208       }
209       else
210       {
211         System.err.println("CMD [-nousagestats] executed successfully!");
212       }
213
214       if (!aparser.contains("noquestionnaire"))
215       {
216         String url = aparser.getValue("questionnaire");
217         if (url != null)
218         {
219           // Start the desktop questionnaire prompter with the specified
220           // questionnaire
221           Cache.log.debug("Starting questionnaire url at " + url);
222           desktop.checkForQuestionnaire(url);
223           System.out.println("CMD questionnaire[-" + url
224                   + "] executed successfully!");
225         }
226         else
227         {
228           if (Cache.getProperty("NOQUESTIONNAIRES") == null)
229           {
230             // Start the desktop questionnaire prompter with the specified
231             // questionnaire
232             // String defurl =
233             // "http://anaplog.compbio.dundee.ac.uk/cgi-bin/questionnaire.pl";
234             // //
235             String defurl = "http://www.jalview.org/cgi-bin/questionnaire.pl";
236             Cache.log.debug("Starting questionnaire with default url: "
237                     + defurl);
238             desktop.checkForQuestionnaire(defurl);
239
240           }
241         }
242       }
243       else
244       {
245         System.err.println("CMD [-noquestionnaire] executed successfully!");
246       }
247       desktop.checkForNews();
248     }
249
250     if (!isHeadlessMode())
251     {
252       BioJsHTMLOutput.updateBioJS();
253     }
254
255     String file = null, protocol = null, format = null, data = null;
256     jalview.io.FileLoader fileLoader = new jalview.io.FileLoader(!headless);
257     Vector getFeatures = null; // vector of das source nicknames to fetch
258     // features from
259     // loading is done.
260     String groovyscript = null; // script to execute after all loading is
261     // completed one way or another
262     // extract groovy argument and execute if necessary
263     groovyscript = aparser.getValue("groovy", true);
264     file = aparser.getValue("open", true);
265
266     if (file == null && desktop == null)
267     {
268       System.out.println("No files to open!");
269       System.exit(1);
270     }
271     String vamsasImport = aparser.getValue("vdoc"), vamsasSession = aparser
272             .getValue("vsess");
273     if (vamsasImport != null || vamsasSession != null)
274     {
275       if (desktop == null || headless)
276       {
277         System.out
278                 .println("Headless vamsas sessions not yet supported. Sorry.");
279         System.exit(1);
280       }
281       // if we have a file, start a new session and import it.
282       boolean inSession = false;
283       if (vamsasImport != null)
284       {
285         try
286         {
287           String viprotocol = jalview.io.AppletFormatAdapter
288                   .checkProtocol(vamsasImport);
289           if (viprotocol == jalview.io.FormatAdapter.FILE)
290           {
291             inSession = desktop.vamsasImport(new File(vamsasImport));
292           }
293           else if (viprotocol == jalview.io.FormatAdapter.URL)
294           {
295             inSession = desktop.vamsasImport(new URL(vamsasImport));
296           }
297
298         } catch (Exception e)
299         {
300           System.err.println("Exeption when importing " + vamsasImport
301                   + " as a vamsas document.");
302           e.printStackTrace();
303         }
304         if (!inSession)
305         {
306           System.err.println("Failed to import " + vamsasImport
307                   + " as a vamsas document.");
308         }
309         else
310         {
311           System.out.println("Imported Successfully into new session "
312                   + desktop.getVamsasApplication().getCurrentSession());
313         }
314       }
315       if (vamsasSession != null)
316       {
317         if (vamsasImport != null)
318         {
319           // close the newly imported session and import the Jalview specific
320           // remnants into the new session later on.
321           desktop.vamsasStop_actionPerformed(null);
322         }
323         // now join the new session
324         try
325         {
326           if (desktop.joinVamsasSession(vamsasSession))
327           {
328             System.out.println("Successfully joined vamsas session "
329                     + vamsasSession);
330           }
331           else
332           {
333             System.err.println("WARNING: Failed to join vamsas session "
334                     + vamsasSession);
335           }
336         } catch (Exception e)
337         {
338           System.err.println("ERROR: Failed to join vamsas session "
339                   + vamsasSession);
340           e.printStackTrace();
341         }
342         if (vamsasImport != null)
343         {
344           // the Jalview specific remnants can now be imported into the new
345           // session at the user's leisure.
346           Cache.log
347                   .info("Skipping Push for import of data into existing vamsas session."); // TODO:
348           // enable
349           // this
350           // when
351           // debugged
352           // desktop.getVamsasApplication().push_update();
353         }
354       }
355     }
356     long progress = -1;
357     // Finally, deal with the remaining input data.
358     if (file != null)
359     {
360       if (!headless)
361       {
362         desktop.setProgressBar(MessageManager
363                 .getString("status.processing_commandline_args"),
364                 progress = System.currentTimeMillis());
365       }
366       System.out.println("CMD [-open " + file + "] executed successfully!");
367
368       if (!file.startsWith("http://"))
369       {
370         if (!(new java.io.File(file)).exists())
371         {
372           System.out.println("Can't find " + file);
373           if (headless)
374           {
375             System.exit(1);
376           }
377         }
378       }
379
380       protocol = jalview.io.AppletFormatAdapter.checkProtocol(file);
381
382       format = new jalview.io.IdentifyFile().identify(file, protocol);
383
384       AlignFrame af = fileLoader.LoadFileWaitTillLoaded(file, protocol,
385               format);
386       if (af == null)
387       {
388         System.out.println("error");
389       }
390       else
391       {
392         Desktop.setCurrentAlignFrame(af);
393         data = aparser.getValue("colour", true);
394         if (data != null)
395         {
396           data.replaceAll("%20", " ");
397
398           jalview.schemes.ColourSchemeI cs = jalview.schemes.ColourSchemeProperty
399                   .getColour(af.getViewport().getAlignment(), data);
400
401           if (cs == null)
402           {
403             jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme(
404                     "white");
405             ucs.parseAppletParameter(data);
406             cs = ucs;
407           }
408           else
409           {
410             System.out.println("CMD [-color " + data
411                     + "] executed successfully!");
412           }
413           af.changeColour(cs);
414         }
415
416         // Must maintain ability to use the groups flag
417         data = aparser.getValue("groups", true);
418         if (data != null)
419         {
420           af.parseFeaturesFile(data,
421                   jalview.io.AppletFormatAdapter.checkProtocol(data));
422           // System.out.println("Added " + data);
423           System.out.println("CMD groups[-" + data
424                   + "]  executed successfully!");
425         }
426         data = aparser.getValue("features", true);
427         if (data != null)
428         {
429           af.parseFeaturesFile(data,
430                   jalview.io.AppletFormatAdapter.checkProtocol(data));
431           // System.out.println("Added " + data);
432           System.out.println("CMD [-features " + data
433                   + "]  executed successfully!");
434         }
435
436         data = aparser.getValue("annotations", true);
437         if (data != null)
438         {
439           af.loadJalviewDataFile(data, null, null, null);
440           // System.out.println("Added " + data);
441           System.out.println("CMD [-annotations " + data
442                   + "] executed successfully!");
443         }
444         // set or clear the sortbytree flag.
445         if (aparser.contains("sortbytree"))
446         {
447           af.getViewport().setSortByTree(true);
448           if (af.getViewport().getSortByTree())
449           {
450             System.out.println("CMD [-sortbytree] executed successfully!");
451           }
452         }
453         if (aparser.contains("no-annotation"))
454         {
455           af.getViewport().setShowAnnotation(false);
456           if (!af.getViewport().isShowAnnotation())
457           {
458             System.out.println("CMD no-annotation executed successfully!");
459           }
460         }
461         if (aparser.contains("nosortbytree"))
462         {
463           af.getViewport().setSortByTree(false);
464           if (!af.getViewport().getSortByTree())
465           {
466             System.out
467                     .println("CMD [-nosortbytree] executed successfully!");
468           }
469         }
470         data = aparser.getValue("tree", true);
471         if (data != null)
472         {
473           jalview.io.NewickFile fin = null;
474           try
475           {
476             System.out.println("CMD [-tree " + data
477                     + "] executed successfully!");
478             fin = new jalview.io.NewickFile(data,
479                     jalview.io.AppletFormatAdapter.checkProtocol(data));
480             if (fin != null)
481             {
482               af.getViewport().setCurrentTree(
483                       af.ShowNewickTree(fin, data).getTree());
484             }
485           } catch (IOException ex)
486           {
487             System.err.println("Couldn't add tree " + data);
488             ex.printStackTrace(System.err);
489           }
490         }
491         // TODO - load PDB structure(s) to alignment JAL-629
492         // (associate with identical sequence in alignment, or a specified
493         // sequence)
494
495         getFeatures = checkDasArguments(aparser);
496         if (af != null && getFeatures != null)
497         {
498           FeatureFetcher ff = startFeatureFetching(getFeatures);
499           if (ff != null)
500           {
501             while (!ff.allFinished() || af.operationInProgress())
502             {
503               // wait around until fetching is finished.
504               try
505               {
506                 Thread.sleep(100);
507               } catch (Exception e)
508               {
509
510               }
511             }
512           }
513           getFeatures = null; // have retrieved features - forget them now.
514         }
515         if (groovyscript != null)
516         {
517           // Execute the groovy script after we've done all the rendering stuff
518           // and before any images or figures are generated.
519           System.out.println("Executing script " + groovyscript);
520           executeGroovyScript(groovyscript, new Object[] { desktop, af });
521           System.out.println("CMD groovy[" + groovyscript
522                   + "] executed successfully!");
523           groovyscript = null;
524         }
525         String imageName = "unnamed.png";
526         while (aparser.getSize() > 1)
527         {
528           format = aparser.nextValue();
529           file = aparser.nextValue();
530
531           if (format.equalsIgnoreCase("png"))
532           {
533             af.createPNG(new java.io.File(file));
534             imageName = (new java.io.File(file)).getName();
535             System.out.println("Creating PNG image: " + file);
536             continue;
537           }
538           else if (format.equalsIgnoreCase("svg"))
539           {
540             File imageFile = new java.io.File(file);
541             imageName = imageFile.getName();
542             af.createSVG(imageFile);
543             System.out.println("Creating SVG image: " + file);
544             continue;
545           }
546           else if (format.equalsIgnoreCase("html"))
547           {
548             File imageFile = new java.io.File(file);
549             imageName = imageFile.getName();
550             new HtmlSvgOutput(new java.io.File(file), af.alignPanel);
551             System.out.println("Creating HTML image: " + file);
552             continue;
553           }
554           else if (format.equalsIgnoreCase("imgMap"))
555           {
556             af.createImageMap(new java.io.File(file), imageName);
557             System.out.println("Creating image map: " + file);
558             continue;
559           }
560           else if (format.equalsIgnoreCase("eps"))
561           {
562             File outputFile = new java.io.File(file);
563             System.out.println("Creating EPS file: "
564                     + outputFile.getAbsolutePath());
565             af.createEPS(outputFile);
566             continue;
567           }
568
569           if (af.saveAlignment(file, format))
570           {
571             System.out.println("Written alignment in " + format
572                     + " format to " + file);
573           }
574           else
575           {
576             System.out.println("Error writing file " + file + " in "
577                     + format + " format!!");
578           }
579
580         }
581
582         while (aparser.getSize() > 0)
583         {
584           System.out.println("Unknown arg: " + aparser.nextValue());
585         }
586       }
587     }
588     AlignFrame startUpAlframe = null;
589     // We'll only open the default file if the desktop is visible.
590     // And the user
591     // ////////////////////
592
593     if (!headless && file == null && vamsasImport == null
594             && jalview.bin.Cache.getDefault("SHOW_STARTUP_FILE", true))
595     {
596       file = jalview.bin.Cache.getDefault(
597               "STARTUP_FILE",
598               jalview.bin.Cache.getDefault("www.jalview.org",
599                       "http://www.jalview.org")
600                       + "/examples/exampleFile_2_7.jar");
601       if (file.equals("http://www.jalview.org/examples/exampleFile_2_3.jar"))
602       {
603         // hardwire upgrade of the startup file
604         file.replace("_2_3.jar", "_2_7.jar");
605         // and remove the stale setting
606         jalview.bin.Cache.removeProperty("STARTUP_FILE");
607       }
608
609       protocol = "File";
610
611       if (file.indexOf("http:") > -1)
612       {
613         protocol = "URL";
614       }
615
616       if (file.endsWith(".jar"))
617       {
618         format = "Jalview";
619       }
620       else
621       {
622         format = new jalview.io.IdentifyFile().identify(file, protocol);
623       }
624
625       startUpAlframe = fileLoader.LoadFileWaitTillLoaded(file, protocol,
626               format);
627       getFeatures = checkDasArguments(aparser);
628       // extract groovy arguments before anything else.
629     }
630     // If the user has specified features to be retrieved,
631     // or a groovy script to be executed, do them if they
632     // haven't been done already
633     // fetch features for the default alignment
634     if (getFeatures != null)
635     {
636       if (startUpAlframe != null)
637       {
638         startFeatureFetching(getFeatures);
639       }
640     }
641     // Once all other stuff is done, execute any groovy scripts (in order)
642     if (groovyscript != null)
643     {
644       if (jalview.bin.Cache.groovyJarsPresent())
645       {
646         System.out.println("Executing script " + groovyscript);
647         executeGroovyScript(groovyscript, new Object[] { desktop,
648             startUpAlframe });
649       }
650       else
651       {
652         System.err
653                 .println("Sorry. Groovy Support is not available, so ignoring the provided groovy script "
654                         + groovyscript);
655       }
656     }
657     // and finally, turn off batch mode indicator - if the desktop still exists
658     if (desktop != null)
659     {
660       if (progress != -1)
661       {
662         desktop.setProgressBar(null, progress);
663       }
664       desktop.setInBatchMode(false);
665     }
666   }
667
668   private static void showUsage()
669   {
670     System.out
671             .println("Usage: jalview -open [FILE] [OUTPUT_FORMAT] [OUTPUT_FILE]\n\n"
672                     + "-nodisplay\tRun Jalview without User Interface.\n"
673                     + "-props FILE\tUse the given Jalview properties file instead of users default.\n"
674                     + "-colour COLOURSCHEME\tThe colourscheme to be applied to the alignment\n"
675                     + "-annotations FILE\tAdd precalculated annotations to the alignment.\n"
676                     + "-tree FILE\tLoad the given newick format tree file onto the alignment\n"
677                     + "-features FILE\tUse the given file to mark features on the alignment.\n"
678                     + "-fasta FILE\tCreate alignment file FILE in Fasta format.\n"
679                     + "-clustal FILE\tCreate alignment file FILE in Clustal format.\n"
680                     + "-pfam FILE\tCreate alignment file FILE in PFAM format.\n"
681                     + "-msf FILE\tCreate alignment file FILE in MSF format.\n"
682                     + "-pileup FILE\tCreate alignment file FILE in Pileup format\n"
683                     + "-pir FILE\tCreate alignment file FILE in PIR format.\n"
684                     + "-blc FILE\tCreate alignment file FILE in BLC format.\n"
685                     + "-json FILE\tCreate alignment file FILE in JSON format.\n"
686                     + "-jalview FILE\tCreate alignment file FILE in Jalview format.\n"
687                     + "-png FILE\tCreate PNG image FILE from alignment.\n"
688                     + "-svg FILE\tCreate SVG image FILE from alignment.\n"
689                     + "-html FILE\tCreate HTML file from alignment.\n"
690                     + "-imgMap FILE\tCreate HTML file FILE with image map of PNG image.\n"
691                     + "-eps FILE\tCreate EPS file FILE from alignment.\n"
692                     + "-questionnaire URL\tQueries the given URL for information about any Jalview user questionnaires.\n"
693                     + "-noquestionnaire\tTurn off questionnaire check.\n"
694                     + "-nousagestats\tTurn off google analytics tracking for this session.\n"
695                     + "-sortbytree OR -nosortbytree\tEnable or disable sorting of the given alignment by the given tree\n"
696                     // +
697                     // "-setprop PROPERTY=VALUE\tSet the given Jalview property, after all other properties files have been read\n\t (quote the 'PROPERTY=VALUE' pair to ensure spaces are passed in correctly)"
698                     + "-jabaws URL\tSpecify URL for Jabaws services (e.g. for a local installation).\n"
699                     + "-dasserver nickname=URL\tAdd and enable a das server with given nickname\n\t\t\t(alphanumeric or underscores only) for retrieval of features for all alignments.\n"
700                     + "\t\t\tSources that also support the sequence command may be specified by prepending the URL with sequence:\n"
701                     + "\t\t\t e.g. sequence:http://localdas.somewhere.org/das/source)\n"
702                     + "-fetchfrom nickname\tQuery nickname for features for the alignments and display them.\n"
703                     // +
704                     // "-vdoc vamsas-document\tImport vamsas document into new session or join existing session with same URN\n"
705                     // + "-vses vamsas-session\tJoin session with given URN\n"
706                     + "-groovy FILE\tExecute groovy script in FILE, after all other arguments have been processed (if FILE is the text 'STDIN' then the file will be read from STDIN)\n"
707                     + "\n~Read documentation in Application or visit http://www.jalview.org for description of Features and Annotations file~\n\n");
708   }
709
710   private static void startUsageStats(final Desktop desktop)
711   {
712     /**
713      * start a User Config prompt asking if we can log usage statistics.
714      */
715     jalview.gui.PromptUserConfig prompter = new jalview.gui.PromptUserConfig(
716             desktop.desktop,
717             "USAGESTATS",
718             "Jalview Usage Statistics",
719             "Do you want to help make Jalview better by enabling "
720                     + "the collection of usage statistics with Google Analytics ?"
721                     + "\n\n(you can enable or disable usage tracking in the preferences)",
722             new Runnable()
723             {
724               @Override
725               public void run()
726               {
727                 Cache.log
728                         .debug("Initialising googletracker for usage stats.");
729                 Cache.initGoogleTracker();
730                 Cache.log.debug("Tracking enabled.");
731               }
732             }, new Runnable()
733             {
734               @Override
735               public void run()
736               {
737                 Cache.log.debug("Not enabling Google Tracking.");
738               }
739             }, null, true);
740     desktop.addDialogThread(prompter);
741   }
742
743   /**
744    * Locate the given string as a file and pass it to the groovy interpreter.
745    * 
746    * @param groovyscript
747    *          the script to execute
748    * @param jalviewContext
749    *          the Jalview Desktop object passed in to the groovy binding as the
750    *          'Jalview' object.
751    */
752   private static void executeGroovyScript(String groovyscript,
753           Object[] jalviewContext)
754   {
755     if (jalviewContext == null)
756     {
757       System.err
758               .println("Sorry. Groovy support is currently only available when running with the Jalview GUI enabled.");
759     }
760     /**
761      * for scripts contained in files
762      */
763     File tfile = null;
764     /**
765      * script's URI
766      */
767     URL sfile = null;
768     if (groovyscript.trim().equals("STDIN"))
769     {
770       // read from stdin into a tempfile and execute it
771       try
772       {
773         tfile = File.createTempFile("jalview", "groovy");
774         PrintWriter outfile = new PrintWriter(new OutputStreamWriter(
775                 new FileOutputStream(tfile)));
776         BufferedReader br = new BufferedReader(
777                 new java.io.InputStreamReader(System.in));
778         String line = null;
779         while ((line = br.readLine()) != null)
780         {
781           outfile.write(line + "\n");
782         }
783         br.close();
784         outfile.flush();
785         outfile.close();
786
787       } catch (Exception ex)
788       {
789         System.err.println("Failed to read from STDIN into tempfile "
790                 + ((tfile == null) ? "(tempfile wasn't created)" : tfile
791                         .toString()));
792         ex.printStackTrace();
793         return;
794       }
795       try
796       {
797         sfile = tfile.toURI().toURL();
798       } catch (Exception x)
799       {
800         System.err
801                 .println("Unexpected Malformed URL Exception for temporary file created from STDIN: "
802                         + tfile.toURI());
803         x.printStackTrace();
804         return;
805       }
806     }
807     else
808     {
809       try
810       {
811         sfile = new URI(groovyscript).toURL();
812       } catch (Exception x)
813       {
814         tfile = new File(groovyscript);
815         if (!tfile.exists())
816         {
817           System.err.println("File '" + groovyscript + "' does not exist.");
818           return;
819         }
820         if (!tfile.canRead())
821         {
822           System.err.println("File '" + groovyscript + "' cannot be read.");
823           return;
824         }
825         if (tfile.length() < 1)
826         {
827           System.err.println("File '" + groovyscript + "' is empty.");
828           return;
829         }
830         try
831         {
832           sfile = tfile.getAbsoluteFile().toURI().toURL();
833         } catch (Exception ex)
834         {
835           System.err.println("Failed to create a file URL for "
836                   + tfile.getAbsoluteFile());
837           return;
838         }
839       }
840     }
841     boolean success = false;
842     try
843     {
844       /*
845        * The following code performs the GroovyScriptEngine invocation using
846        * reflection, and is equivalent to this fragment from the embedding
847        * groovy documentation on the groovy site: <code> import
848        * groovy.lang.Binding; import groovy.util.GroovyScriptEngine;
849        * 
850        * String[] roots = new String[] { "/my/groovy/script/path" };
851        * GroovyScriptEngine gse = new GroovyScriptEngine(roots); Binding binding
852        * = new Binding(); binding.setVariable("input", "world");
853        * gse.run("hello.groovy", binding); </code>
854        */
855       Class<?>[] bspec;
856       Object[] binding;
857       int blen = ((jalviewContext[0] == null) ? 0 : 1)
858               + ((jalviewContext[1] == null) ? 0 : 1);
859       String cnames[] = new String[] { "Jalview", "currentAlFrame" };
860       bspec = new Class[blen * 2];
861       binding = new Object[blen * 2];
862       blen = 0;
863       ClassLoader cl = null;
864       Map<String, Object> vbinding = new HashMap<String, Object>();
865       for (int jc = 0; jc < jalviewContext.length; jc++)
866       {
867         if (jalviewContext[jc] != null)
868         {
869           if (cl == null)
870           {
871             cl = jalviewContext[jc].getClass().getClassLoader();
872           }
873           bspec[blen * 2] = String.class;
874           bspec[blen * 2 + 1] = Object.class;
875           binding[blen * 2] = cnames[jc];
876           binding[blen * 2 + 1] = jalviewContext[jc];
877           vbinding.put(cnames[jc], jalviewContext[jc]);
878           blen++;
879         }
880       }
881       Class<?> gbindingc = cl.loadClass("groovy.lang.Binding");
882       Constructor<?> gbcons;
883       Object gbinding;
884       try
885       {
886         gbcons = gbindingc.getConstructor(Map.class);
887         gbinding = gbcons.newInstance(vbinding);
888       } catch (NoSuchMethodException x)
889       {
890         // old style binding config - using series of string/object values to
891         // setVariable.
892         gbcons = gbindingc.getConstructor();
893         gbinding = gbcons.newInstance();
894         java.lang.reflect.Method setvar = gbindingc.getMethod(
895                 "setVariable", bspec);
896         setvar.invoke(gbinding, binding);
897       }
898
899       Class<?> gsec = cl.loadClass("groovy.util.GroovyScriptEngine");
900       Constructor<?> gseccons = gsec
901               .getConstructor(new Class[] { URL[].class }); // String[].class
902                                                             // });
903       Object gse = gseccons
904               .newInstance(new Object[] { new URL[] { sfile } }); // .toString()
905                                                                   // } });
906       java.lang.reflect.Method run = gsec.getMethod("run", new Class[] {
907           String.class, gbindingc });
908       run.invoke(gse, new Object[] { sfile.toString(), gbinding });
909       success = true;
910     } catch (Exception e)
911     {
912       System.err.println("Exception Whilst trying to execute file " + sfile
913               + " as a groovy script.");
914       e.printStackTrace(System.err);
915
916     }
917     if (success && groovyscript.equals("STDIN"))
918     {
919       // delete temp file that we made - but only if it was successfully
920       // executed
921       tfile.delete();
922     }
923   }
924
925   /**
926    * Check commandline for any das server definitions or any fetchfrom switches
927    * 
928    * @return vector of DAS source nicknames to retrieve from
929    */
930   private static Vector checkDasArguments(ArgsParser aparser)
931   {
932     Vector source = null;
933     String data;
934     String locsources = Cache.getProperty(Cache.DAS_LOCAL_SOURCE);
935     while ((data = aparser.getValue("dasserver", true)) != null)
936     {
937       String nickname = null;
938       String url = null;
939       boolean seq = false, feat = true;
940       int pos = data.indexOf('=');
941       // determine capabilities
942       if (pos > 0)
943       {
944         nickname = data.substring(0, pos);
945       }
946       url = data.substring(pos + 1);
947       if (url != null
948               && (url.startsWith("http:") || url
949                       .startsWith("sequence:http:")))
950       {
951         if (nickname == null)
952         {
953           nickname = url;
954         }
955         if (locsources == null)
956         {
957           locsources = "";
958         }
959         else
960         {
961           locsources += "\t";
962         }
963         locsources = locsources + nickname + "|" + url;
964         System.err
965                 .println("NOTE! dasserver parameter not yet really supported (got args of "
966                         + nickname + "|" + url);
967         if (source == null)
968         {
969           source = new Vector();
970         }
971         source.addElement(nickname);
972       }
973       System.out.println("CMD [-dasserver " + data
974               + "] executed successfully!");
975     } // loop until no more server entries are found.
976     if (locsources != null && locsources.indexOf('|') > -1)
977     {
978       Cache.log.debug("Setting local source list in properties file to:\n"
979               + locsources);
980       Cache.setProperty(Cache.DAS_LOCAL_SOURCE, locsources);
981     }
982     while ((data = aparser.getValue("fetchfrom", true)) != null)
983     {
984       System.out.println("adding source '" + data + "'");
985       if (source == null)
986       {
987         source = new Vector();
988       }
989       source.addElement(data);
990     }
991     return source;
992   }
993
994   /**
995    * start a feature fetcher for every alignment frame
996    * 
997    * @param dasSources
998    */
999   private static FeatureFetcher startFeatureFetching(final Vector dasSources)
1000   {
1001     FeatureFetcher ff = new FeatureFetcher();
1002     AlignFrame afs[] = Desktop.getAlignFrames();
1003     if (afs == null || afs.length == 0)
1004     {
1005       return null;
1006     }
1007     for (int i = 0; i < afs.length; i++)
1008     {
1009       ff.addFetcher(afs[i], dasSources);
1010     }
1011     return ff;
1012   }
1013
1014   public static boolean isHeadlessMode()
1015   {
1016     String isheadless = System.getProperty("java.awt.headless");
1017     if (isheadless != null && isheadless.equalsIgnoreCase("true"))
1018     {
1019       return true;
1020     }
1021     return false;
1022   }
1023 }
1024
1025 /**
1026  * Notes: this argParser does not distinguish between parameter switches,
1027  * parameter values and argument text. If an argument happens to be identical to
1028  * a parameter, it will be taken as such (even though it didn't have a '-'
1029  * prefixing it).
1030  * 
1031  * @author Andrew Waterhouse and JBP documented.
1032  * 
1033  */
1034
1035 class rnabuttonlistener implements ActionListener
1036 {
1037   @Override
1038   public void actionPerformed(ActionEvent arg0)
1039   {
1040     System.out.println("Good idea ! ");
1041
1042   }
1043 }
1044
1045 class pbuttonlistener implements ActionListener
1046 {
1047   @Override
1048   public void actionPerformed(ActionEvent arg0)
1049   {
1050
1051   }
1052 }
1053
1054 class ArgsParser
1055 {
1056   Vector vargs = null;
1057
1058   public ArgsParser(String[] args)
1059   {
1060     vargs = new Vector();
1061     for (int i = 0; i < args.length; i++)
1062     {
1063       String arg = args[i].trim();
1064       if (arg.charAt(0) == '-')
1065       {
1066         arg = arg.substring(1);
1067       }
1068       vargs.addElement(arg);
1069     }
1070   }
1071
1072   /**
1073    * check for and remove first occurence of arg+parameter in arglist.
1074    * 
1075    * @param arg
1076    * @return return the argument following the given arg if arg was in list.
1077    */
1078   public String getValue(String arg)
1079   {
1080     return getValue(arg, false);
1081   }
1082
1083   public String getValue(String arg, boolean utf8decode)
1084   {
1085     int index = vargs.indexOf(arg);
1086     String dc = null, ret = null;
1087     if (index != -1)
1088     {
1089       ret = vargs.elementAt(index + 1).toString();
1090       vargs.removeElementAt(index);
1091       vargs.removeElementAt(index);
1092       if (utf8decode && ret != null)
1093       {
1094         try
1095         {
1096           dc = URLDecoder.decode(ret, "UTF-8");
1097           ret = dc;
1098         } catch (Exception e)
1099         {
1100           // TODO: log failure to decode
1101         }
1102       }
1103     }
1104     return ret;
1105   }
1106
1107   /**
1108    * check for and remove first occurence of arg in arglist.
1109    * 
1110    * @param arg
1111    * @return true if arg was present in argslist.
1112    */
1113   public boolean contains(String arg)
1114   {
1115     if (vargs.contains(arg))
1116     {
1117       vargs.removeElement(arg);
1118       return true;
1119     }
1120     else
1121     {
1122       return false;
1123     }
1124   }
1125
1126   public String nextValue()
1127   {
1128     return vargs.remove(0).toString();
1129   }
1130
1131   public int getSize()
1132   {
1133     return vargs.size();
1134   }
1135
1136 }
1137
1138 /**
1139  * keep track of feature fetching tasks.
1140  * 
1141  * @author JimP
1142  * 
1143  */
1144 class FeatureFetcher
1145 {
1146   /*
1147    * TODO: generalise to track all jalview events to orchestrate batch
1148    * processing events.
1149    */
1150
1151   private int queued = 0;
1152
1153   private int running = 0;
1154
1155   public FeatureFetcher()
1156   {
1157
1158   }
1159
1160   public void addFetcher(final AlignFrame af, final Vector dasSources)
1161   {
1162     final long id = System.currentTimeMillis();
1163     queued++;
1164     final FeatureFetcher us = this;
1165     new Thread(new Runnable()
1166     {
1167
1168       @Override
1169       public void run()
1170       {
1171         synchronized (us)
1172         {
1173           queued--;
1174           running++;
1175         }
1176
1177         af.setProgressBar(MessageManager
1178                 .getString("status.das_features_being_retrived"), id);
1179         af.featureSettings_actionPerformed(null);
1180         af.featureSettings.fetchDasFeatures(dasSources, true);
1181         af.setProgressBar(null, id);
1182         synchronized (us)
1183         {
1184           running--;
1185         }
1186       }
1187     }).start();
1188   }
1189
1190   public synchronized boolean allFinished()
1191   {
1192     return queued == 0 && running == 0;
1193   }
1194
1195 }