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