Merge branch 'develop' into task/JAL-3991_check_actual_JAVA_VERSION_against_build_pro...
[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 java.io.BufferedReader;
24 import java.io.File;
25 import java.io.FileOutputStream;
26 import java.io.IOException;
27 import java.io.InputStreamReader;
28 import java.io.OutputStreamWriter;
29 import java.io.PrintWriter;
30 import java.net.MalformedURLException;
31 import java.net.URI;
32 import java.net.URISyntaxException;
33 import java.net.URL;
34 import java.security.AllPermission;
35 import java.security.CodeSource;
36 import java.security.PermissionCollection;
37 import java.security.Permissions;
38 import java.security.Policy;
39 import java.util.HashMap;
40 import java.util.Locale;
41 import java.util.Map;
42 import java.util.Vector;
43 import java.util.logging.ConsoleHandler;
44 import java.util.logging.Level;
45 import java.util.logging.Logger;
46
47 import javax.swing.JOptionPane;
48 import javax.swing.UIManager;
49 import javax.swing.UIManager.LookAndFeelInfo;
50
51 import com.threerings.getdown.util.LaunchUtil;
52
53 //import edu.stanford.ejalbert.launching.IBrowserLaunching;
54 import groovy.lang.Binding;
55 import groovy.util.GroovyScriptEngine;
56 import jalview.ext.so.SequenceOntology;
57 import jalview.gui.AlignFrame;
58 import jalview.gui.Desktop;
59 import jalview.gui.PromptUserConfig;
60 import jalview.io.AppletFormatAdapter;
61 import jalview.io.BioJsHTMLOutput;
62 import jalview.io.DataSourceType;
63 import jalview.io.FileFormat;
64 import jalview.io.FileFormatException;
65 import jalview.io.FileFormatI;
66 import jalview.io.FileFormats;
67 import jalview.io.FileLoader;
68 import jalview.io.HtmlSvgOutput;
69 import jalview.io.IdentifyFile;
70 import jalview.io.NewickFile;
71 import jalview.io.gff.SequenceOntologyFactory;
72 import jalview.schemes.ColourSchemeI;
73 import jalview.schemes.ColourSchemeProperty;
74 import jalview.util.ChannelProperties;
75 import jalview.util.HttpUtils;
76 import jalview.util.LaunchUtils;
77 import jalview.util.MessageManager;
78 import jalview.util.Platform;
79 import jalview.ws.jws2.Jws2Discoverer;
80
81 /**
82  * Main class for Jalview Application <br>
83  * <br>
84  * start with: java -classpath "$PATH_TO_LIB$/*:$PATH_TO_CLASSES$" \
85  * jalview.bin.Jalview
86  * 
87  * or on Windows: java -classpath "$PATH_TO_LIB$/*;$PATH_TO_CLASSES$" \
88  * jalview.bin.Jalview jalview.bin.Jalview
89  * 
90  * (ensure -classpath arg is quoted to avoid shell expansion of '*' and do not
91  * embellish '*' to e.g. '*.jar')
92  * 
93  * @author $author$
94  * @version $Revision$
95  */
96 public class Jalview
97 {
98   static
99   {
100     Platform.getURLCommandArguments();
101     Platform.addJ2SDirectDatabaseCall("https://www.jalview.org");
102     Platform.addJ2SDirectDatabaseCall("http://www.jalview.org");
103     Platform.addJ2SDirectDatabaseCall("http://www.compbio.dundee.ac.uk");
104     Platform.addJ2SDirectDatabaseCall("https://www.compbio.dundee.ac.uk");
105   }
106
107   /*
108    * singleton instance of this class
109    */
110   private static Jalview instance;
111
112   private Desktop desktop;
113
114   public static AlignFrame currentAlignFrame;
115
116   static
117   {
118     if (!Platform.isJS())
119     /**
120      * Java only
121      * 
122      * @j2sIgnore
123      */
124     {
125       // grab all the rights we can for the JVM
126       Policy.setPolicy(new Policy()
127       {
128         @Override
129         public PermissionCollection getPermissions(CodeSource codesource)
130         {
131           Permissions perms = new Permissions();
132           perms.add(new AllPermission());
133           return (perms);
134         }
135
136         @Override
137         public void refresh()
138         {
139         }
140       });
141     }
142   }
143
144   /**
145    * keep track of feature fetching tasks.
146    * 
147    * @author JimP
148    * 
149    */
150   class FeatureFetcher
151   {
152     /*
153      * TODO: generalise to track all jalview events to orchestrate batch processing
154      * events.
155      */
156
157     private int queued = 0;
158
159     private int running = 0;
160
161     public FeatureFetcher()
162     {
163
164     }
165
166     public void addFetcher(final AlignFrame af,
167             final Vector<String> dasSources)
168     {
169       final long id = System.currentTimeMillis();
170       queued++;
171       final FeatureFetcher us = this;
172       new Thread(new Runnable()
173       {
174
175         @Override
176         public void run()
177         {
178           synchronized (us)
179           {
180             queued--;
181             running++;
182           }
183
184           af.setProgressBar(MessageManager
185                   .getString("status.das_features_being_retrived"), id);
186           af.featureSettings_actionPerformed(null);
187           af.setProgressBar(null, id);
188           synchronized (us)
189           {
190             running--;
191           }
192         }
193       }).start();
194     }
195
196     public synchronized boolean allFinished()
197     {
198       return queued == 0 && running == 0;
199     }
200
201   }
202
203   public static Jalview getInstance()
204   {
205     return instance;
206   }
207
208   /**
209    * main class for Jalview application
210    * 
211    * @param args
212    *          open <em>filename</em>
213    */
214   public static void main(String[] args)
215   {
216     // setLogging(); // BH - for event debugging in JavaScript
217     instance = new Jalview();
218     instance.doMain(args);
219   }
220
221   private static void logClass(String name)
222   {
223     // BH - for event debugging in JavaScript
224     ConsoleHandler consoleHandler = new ConsoleHandler();
225     consoleHandler.setLevel(Level.ALL);
226     Logger logger = Logger.getLogger(name);
227     logger.setLevel(Level.ALL);
228     logger.addHandler(consoleHandler);
229   }
230
231   @SuppressWarnings("unused")
232   private static void setLogging()
233   {
234
235     /**
236      * @j2sIgnore
237      * 
238      */
239     {
240       System.out.println("not in js");
241     }
242
243     // BH - for event debugging in JavaScript (Java mode only)
244     if (!Platform.isJS())
245     /**
246      * Java only
247      * 
248      * @j2sIgnore
249      */
250     {
251       Logger.getLogger("").setLevel(Level.ALL);
252       logClass("java.awt.EventDispatchThread");
253       logClass("java.awt.EventQueue");
254       logClass("java.awt.Component");
255       logClass("java.awt.focus.Component");
256       logClass("java.awt.focus.DefaultKeyboardFocusManager");
257     }
258
259   }
260
261   /**
262    * @param args
263    */
264   void doMain(String[] args)
265   {
266
267     if (!Platform.isJS())
268     {
269       System.setSecurityManager(null);
270     }
271
272     System.out
273             .println("Java version: " + System.getProperty("java.version"));
274     System.out.println("Java Home: " + System.getProperty("java.home"));
275     System.out.println(System.getProperty("os.arch") + " "
276             + System.getProperty("os.name") + " "
277             + System.getProperty("os.version"));
278     String val = System.getProperty("sys.install4jVersion");
279     if (val != null)
280     {
281       System.out.println("Install4j version: " + val);
282     }
283     val = System.getProperty("installer_template_version");
284     if (val != null)
285     {
286       System.out.println("Install4j template version: " + val);
287     }
288     val = System.getProperty("launcher_version");
289     if (val != null)
290     {
291       System.out.println("Launcher version: " + val);
292     }
293
294     // report Jalview version
295     Cache.loadBuildProperties(true);
296
297     ArgsParser aparser = new ArgsParser(args);
298     boolean headless = false;
299
300     String usrPropsFile = aparser.getValue("props");
301     Cache.loadProperties(usrPropsFile); // must do this before
302     if (usrPropsFile != null)
303     {
304       System.out.println(
305               "CMD [-props " + usrPropsFile + "] executed successfully!");
306     }
307
308     if (!Platform.isJS())
309     /**
310      * Java only
311      * 
312      * @j2sIgnore
313      */
314     {
315       if (aparser.contains("help") || aparser.contains("h"))
316       {
317         showUsage();
318         System.exit(0);
319       }
320       if (aparser.contains("nodisplay") || aparser.contains("nogui")
321               || aparser.contains("headless"))
322       {
323         System.setProperty("java.awt.headless", "true");
324         headless = true;
325       }
326       // anything else!
327
328       // allow https handshakes to download intermediate certs if necessary
329       System.setProperty("com.sun.security.enableAIAcaIssuers", "true");
330
331       final String jabawsUrl = aparser.getValue("jabaws");
332       if (jabawsUrl != null)
333       {
334         try
335         {
336           Jws2Discoverer.getDiscoverer().setPreferredUrl(jabawsUrl);
337           System.out.println(
338                   "CMD [-jabaws " + jabawsUrl + "] executed successfully!");
339         } catch (MalformedURLException e)
340         {
341           System.err.println(
342                   "Invalid jabaws parameter: " + jabawsUrl + " ignored");
343         }
344       }
345     }
346
347     String defs = aparser.getValue("setprop");
348     while (defs != null)
349     {
350       int p = defs.indexOf('=');
351       if (p == -1)
352       {
353         System.err.println("Ignoring invalid setprop argument : " + defs);
354       }
355       else
356       {
357         System.out.println("Executing setprop argument: " + defs);
358         if (Platform.isJS())
359         {
360           Cache.setProperty(defs.substring(0, p), defs.substring(p + 1));
361         }
362         // DISABLED FOR SECURITY REASONS
363         // TODO: add a property to allow properties to be overriden by cli args
364         // Cache.setProperty(defs.substring(0,p), defs.substring(p+1));
365       }
366       defs = aparser.getValue("setprop");
367     }
368     if (System.getProperty("java.awt.headless") != null
369             && System.getProperty("java.awt.headless").equals("true"))
370     {
371       headless = true;
372     }
373     System.setProperty("http.agent",
374             "Jalview Desktop/" + Cache.getDefault("VERSION", "Unknown"));
375
376     try
377     {
378       Console.initLogger();
379     } catch (NoClassDefFoundError error)
380     {
381       error.printStackTrace();
382       System.out.println("\nEssential logging libraries not found."
383               + "\nUse: java -classpath \"$PATH_TO_LIB$/*:$PATH_TO_CLASSES$\" jalview.bin.Jalview");
384       System.exit(0);
385     }
386
387     desktop = null;
388
389     setLookAndFeel();
390
391     /*
392      * configure 'full' SO model if preferences say to, else use the default (full SO)
393      * - as JS currently doesn't have OBO parsing, it must use 'Lite' version
394      */
395     boolean soDefault = !Platform.isJS();
396     if (Cache.getDefault("USE_FULL_SO", soDefault))
397     {
398       SequenceOntologyFactory.setInstance(new SequenceOntology());
399     }
400
401     if (!headless)
402     {
403       Desktop.nosplash = aparser.contains("nosplash");
404       desktop = new Desktop();
405       desktop.setInBatchMode(true); // indicate we are starting up
406
407       try
408       {
409         JalviewTaskbar.setTaskbar(this);
410       } catch (Exception e)
411       {
412         Console.info("Cannot set Taskbar");
413         Console.error(e.getMessage());
414         // e.printStackTrace();
415       } catch (Throwable t)
416       {
417         Console.info("Cannot set Taskbar");
418         Console.error(t.getMessage());
419         // t.printStackTrace();
420       }
421
422       // set Proxy settings before all the internet calls
423       Cache.setProxyPropertiesFromPreferences();
424
425       desktop.setVisible(true);
426
427       if (!Platform.isJS())
428       /**
429        * Java only
430        * 
431        * @j2sIgnore
432        */
433       {
434
435         /**
436          * Check to see that the JVM version being run is suitable for the Java
437          * version this Jalview was compiled for. Popup a warning if not.
438          */
439         if (!LaunchUtils.checkJavaVersion())
440         {
441           Console.warn("The Java version being used (Java "
442                   + LaunchUtils.getJavaVersion()
443                   + ") may lead to problems. This installation of Jalview should be used with Java "
444                   + LaunchUtils.getJavaCompileVersion() + ".");
445
446           if (!LaunchUtils
447                   .getBooleanUserPreference("IGNORE_JVM_WARNING_POPUP"))
448           {
449             Object[] options = {
450                 MessageManager.getString("label.continue") };
451             JOptionPane.showOptionDialog(null,
452                     MessageManager.formatMessage(
453                             "warning.wrong_jvm_version_message",
454                             LaunchUtils.getJavaVersion(),
455                             LaunchUtils.getJavaCompileVersion()),
456                     MessageManager
457                             .getString("warning.wrong_jvm_version_title"),
458                     JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE,
459                     null, options, options[0]);
460           }
461         }
462
463         if (!aparser.contains("nowebservicediscovery"))
464         {
465           desktop.startServiceDiscovery();
466         }
467         if (!aparser.contains("nousagestats"))
468         {
469           startUsageStats(desktop);
470         }
471         else
472         {
473           System.err.println("CMD [-nousagestats] executed successfully!");
474         }
475
476         if (!aparser.contains("noquestionnaire"))
477         {
478           String url = aparser.getValue("questionnaire");
479           if (url != null)
480           {
481             // Start the desktop questionnaire prompter with the specified
482             // questionnaire
483             Console.debug("Starting questionnaire url at " + url);
484             desktop.checkForQuestionnaire(url);
485             System.out.println("CMD questionnaire[-" + url
486                     + "] executed successfully!");
487           }
488           else
489           {
490             if (Cache.getProperty("NOQUESTIONNAIRES") == null)
491             {
492               // Start the desktop questionnaire prompter with the specified
493               // questionnaire
494               // String defurl =
495               // "http://anaplog.compbio.dundee.ac.uk/cgi-bin/questionnaire.pl";
496               // //
497               String defurl = "https://www.jalview.org/cgi-bin/questionnaire.pl";
498               Console.debug(
499                       "Starting questionnaire with default url: " + defurl);
500               desktop.checkForQuestionnaire(defurl);
501             }
502           }
503         }
504         else
505         {
506           System.err
507                   .println("CMD [-noquestionnaire] executed successfully!");
508         }
509
510         if (!aparser.contains("nonews")
511                 || Cache.getProperty("NONEWS") == null)
512         {
513           desktop.checkForNews();
514         }
515
516         if (!aparser.contains("nohtmltemplates")
517                 || Cache.getProperty("NOHTMLTEMPLATES") == null)
518         {
519           BioJsHTMLOutput.updateBioJS();
520         }
521       }
522     }
523
524     // Check if JVM and compile version might cause problems and log if it
525     // might.
526     if (headless && !Platform.isJS() && !LaunchUtils.checkJavaVersion())
527     {
528       Console.warn("The Java version being used (Java "
529               + LaunchUtils.getJavaVersion()
530               + ") may lead to problems. This installation of Jalview should be used with Java "
531               + LaunchUtils.getJavaCompileVersion() + ".");
532     }
533
534     // Move any new getdown-launcher-new.jar into place over old
535     // getdown-launcher.jar
536     String appdirString = System.getProperty("getdownappdir");
537     if (appdirString != null && appdirString.length() > 0)
538     {
539       final File appdir = new File(appdirString);
540       new Thread()
541       {
542         @Override
543         public void run()
544         {
545           LaunchUtil.upgradeGetdown(
546                   new File(appdir, "getdown-launcher-old.jar"),
547                   new File(appdir, "getdown-launcher.jar"),
548                   new File(appdir, "getdown-launcher-new.jar"));
549         }
550       }.start();
551     }
552
553     String file = null, data = null;
554     FileFormatI format = null;
555     DataSourceType protocol = null;
556     FileLoader fileLoader = new FileLoader(!headless);
557
558     String groovyscript = null; // script to execute after all loading is
559     // completed one way or another
560     // extract groovy argument and execute if necessary
561     groovyscript = aparser.getValue("groovy", true);
562     file = aparser.getValue("open", true);
563
564     if (file == null && desktop == null)
565     {
566       System.out.println("No files to open!");
567       System.exit(1);
568     }
569     long progress = -1;
570     // Finally, deal with the remaining input data.
571     if (file != null)
572     {
573       if (!headless)
574       {
575         desktop.setProgressBar(
576                 MessageManager
577                         .getString("status.processing_commandline_args"),
578                 progress = System.currentTimeMillis());
579       }
580       System.out.println("CMD [-open " + file + "] executed successfully!");
581
582       if (!Platform.isJS())
583       /**
584        * ignore in JavaScript -- can't just file existence - could load it?
585        * 
586        * @j2sIgnore
587        */
588       {
589         if (!HttpUtils.startsWithHttpOrHttps(file))
590         {
591           if (!(new File(file)).exists())
592           {
593             System.out.println("Can't find " + file);
594             if (headless)
595             {
596               System.exit(1);
597             }
598           }
599         }
600       }
601
602       protocol = AppletFormatAdapter.checkProtocol(file);
603
604       try
605       {
606         format = new IdentifyFile().identify(file, protocol);
607       } catch (FileFormatException e1)
608       {
609         // TODO ?
610       }
611
612       AlignFrame af = fileLoader.LoadFileWaitTillLoaded(file, protocol,
613               format);
614       if (af == null)
615       {
616         System.out.println("error");
617       }
618       else
619       {
620         setCurrentAlignFrame(af);
621         data = aparser.getValue("colour", true);
622         if (data != null)
623         {
624           data.replaceAll("%20", " ");
625
626           ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
627                   af.getViewport(), af.getViewport().getAlignment(), data);
628
629           if (cs != null)
630           {
631             System.out.println(
632                     "CMD [-color " + data + "] executed successfully!");
633           }
634           af.changeColour(cs);
635         }
636
637         // Must maintain ability to use the groups flag
638         data = aparser.getValue("groups", true);
639         if (data != null)
640         {
641           af.parseFeaturesFile(data,
642                   AppletFormatAdapter.checkProtocol(data));
643           // System.out.println("Added " + data);
644           System.out.println(
645                   "CMD groups[-" + data + "]  executed successfully!");
646         }
647         data = aparser.getValue("features", true);
648         if (data != null)
649         {
650           af.parseFeaturesFile(data,
651                   AppletFormatAdapter.checkProtocol(data));
652           // System.out.println("Added " + data);
653           System.out.println(
654                   "CMD [-features " + data + "]  executed successfully!");
655         }
656
657         data = aparser.getValue("annotations", true);
658         if (data != null)
659         {
660           af.loadJalviewDataFile(data, null, null, null);
661           // System.out.println("Added " + data);
662           System.out.println(
663                   "CMD [-annotations " + data + "] executed successfully!");
664         }
665         // set or clear the sortbytree flag.
666         if (aparser.contains("sortbytree"))
667         {
668           af.getViewport().setSortByTree(true);
669           if (af.getViewport().getSortByTree())
670           {
671             System.out.println("CMD [-sortbytree] executed successfully!");
672           }
673         }
674         if (aparser.contains("no-annotation"))
675         {
676           af.getViewport().setShowAnnotation(false);
677           if (!af.getViewport().isShowAnnotation())
678           {
679             System.out.println("CMD no-annotation executed successfully!");
680           }
681         }
682         if (aparser.contains("nosortbytree"))
683         {
684           af.getViewport().setSortByTree(false);
685           if (!af.getViewport().getSortByTree())
686           {
687             System.out
688                     .println("CMD [-nosortbytree] executed successfully!");
689           }
690         }
691         data = aparser.getValue("tree", true);
692         if (data != null)
693         {
694           try
695           {
696             System.out.println(
697                     "CMD [-tree " + data + "] executed successfully!");
698             NewickFile nf = new NewickFile(data,
699                     AppletFormatAdapter.checkProtocol(data));
700             af.getViewport()
701                     .setCurrentTree(af.showNewickTree(nf, data).getTree());
702           } catch (IOException ex)
703           {
704             System.err.println("Couldn't add tree " + data);
705             ex.printStackTrace(System.err);
706           }
707         }
708         // TODO - load PDB structure(s) to alignment JAL-629
709         // (associate with identical sequence in alignment, or a specified
710         // sequence)
711         if (groovyscript != null)
712         {
713           // Execute the groovy script after we've done all the rendering stuff
714           // and before any images or figures are generated.
715           System.out.println("Executing script " + groovyscript);
716           executeGroovyScript(groovyscript, af);
717           System.out.println("CMD groovy[" + groovyscript
718                   + "] executed successfully!");
719           groovyscript = null;
720         }
721         String imageName = "unnamed.png";
722         while (aparser.getSize() > 1)
723         {
724           String outputFormat = aparser.nextValue();
725           file = aparser.nextValue();
726
727           if (outputFormat.equalsIgnoreCase("png"))
728           {
729             af.createPNG(new File(file));
730             imageName = (new File(file)).getName();
731             System.out.println("Creating PNG image: " + file);
732             continue;
733           }
734           else if (outputFormat.equalsIgnoreCase("svg"))
735           {
736             File imageFile = new File(file);
737             imageName = imageFile.getName();
738             af.createSVG(imageFile);
739             System.out.println("Creating SVG image: " + file);
740             continue;
741           }
742           else if (outputFormat.equalsIgnoreCase("html"))
743           {
744             File imageFile = new File(file);
745             imageName = imageFile.getName();
746             HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
747             htmlSVG.exportHTML(file);
748
749             System.out.println("Creating HTML image: " + file);
750             continue;
751           }
752           else if (outputFormat.equalsIgnoreCase("biojsmsa"))
753           {
754             if (file == null)
755             {
756               System.err.println("The output html file must not be null");
757               return;
758             }
759             try
760             {
761               BioJsHTMLOutput.refreshVersionInfo(
762                       BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
763             } catch (URISyntaxException e)
764             {
765               e.printStackTrace();
766             }
767             BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
768             bjs.exportHTML(file);
769             System.out
770                     .println("Creating BioJS MSA Viwer HTML file: " + file);
771             continue;
772           }
773           else if (outputFormat.equalsIgnoreCase("imgMap"))
774           {
775             af.createImageMap(new File(file), imageName);
776             System.out.println("Creating image map: " + file);
777             continue;
778           }
779           else if (outputFormat.equalsIgnoreCase("eps"))
780           {
781             File outputFile = new File(file);
782             System.out.println(
783                     "Creating EPS file: " + outputFile.getAbsolutePath());
784             af.createEPS(outputFile);
785             continue;
786           }
787           FileFormatI outFormat = null;
788           try
789           {
790             outFormat = FileFormats.getInstance().forName(outputFormat);
791           } catch (Exception formatP)
792           {
793             System.out.println("Couldn't parse " + outFormat
794                     + " as a valid Jalview format string.");
795           }
796           if (outFormat != null)
797           {
798             if (!outFormat.isWritable())
799             {
800               System.out.println(
801                       "This version of Jalview does not support alignment export as "
802                               + outputFormat);
803             }
804             else
805             {
806               af.saveAlignment(file, outFormat);
807               if (af.isSaveAlignmentSuccessful())
808               {
809                 System.out.println("Written alignment in "
810                         + outFormat.getName() + " format to " + file);
811               }
812               else
813               {
814                 System.out.println("Error writing file " + file + " in "
815                         + outFormat.getName() + " format!!");
816               }
817             }
818           }
819
820         }
821
822         while (aparser.getSize() > 0)
823         {
824           System.out.println("Unknown arg: " + aparser.nextValue());
825         }
826       }
827     }
828     AlignFrame startUpAlframe = null;
829     // We'll only open the default file if the desktop is visible.
830     // And the user
831     // ////////////////////
832
833     if (!Platform.isJS() && !headless && file == null
834             && Cache.getDefault("SHOW_STARTUP_FILE", true))
835     /**
836      * Java only
837      * 
838      * @j2sIgnore
839      */
840     {
841       file = Cache.getDefault("STARTUP_FILE",
842               Cache.getDefault("www.jalview.org", "https://www.jalview.org")
843                       + "/examples/exampleFile_2_7.jvp");
844       if (file.equals("http://www.jalview.org/examples/exampleFile_2_3.jar")
845               || file.equals(
846                       "http://www.jalview.org/examples/exampleFile_2_7.jar"))
847       {
848         file.replace("http:", "https:");
849         // hardwire upgrade of the startup file
850         file.replace("_2_3", "_2_7");
851         file.replace("2_7.jar", "2_7.jvp");
852         // and remove the stale setting
853         Cache.removeProperty("STARTUP_FILE");
854       }
855
856       protocol = AppletFormatAdapter.checkProtocol(file);
857
858       if (file.endsWith(".jar"))
859       {
860         format = FileFormat.Jalview;
861       }
862       else
863       {
864         try
865         {
866           format = new IdentifyFile().identify(file, protocol);
867         } catch (FileFormatException e)
868         {
869           // TODO what?
870         }
871       }
872
873       startUpAlframe = fileLoader.LoadFileWaitTillLoaded(file, protocol,
874               format);
875       // extract groovy arguments before anything else.
876     }
877
878     // Once all other stuff is done, execute any groovy scripts (in order)
879     if (groovyscript != null)
880     {
881       if (Cache.groovyJarsPresent())
882       {
883         System.out.println("Executing script " + groovyscript);
884         executeGroovyScript(groovyscript, startUpAlframe);
885       }
886       else
887       {
888         System.err.println(
889                 "Sorry. Groovy Support is not available, so ignoring the provided groovy script "
890                         + groovyscript);
891       }
892     }
893     // and finally, turn off batch mode indicator - if the desktop still exists
894     if (desktop != null)
895     {
896       if (progress != -1)
897       {
898         desktop.setProgressBar(null, progress);
899       }
900       desktop.setInBatchMode(false);
901     }
902   }
903
904   private static void setLookAndFeel()
905   {
906     // property laf = "crossplatform", "system", "gtk", "metal", "nimbus" or
907     // "mac"
908     // If not set (or chosen laf fails), use the normal SystemLaF and if on Mac,
909     // try Quaqua/Vaqua.
910     String lafProp = System.getProperty("laf");
911     String lafSetting = Cache.getDefault("PREFERRED_LAF", null);
912     String laf = "none";
913     if (lafProp != null)
914     {
915       laf = lafProp;
916     }
917     else if (lafSetting != null)
918     {
919       laf = lafSetting;
920     }
921     boolean lafSet = false;
922     switch (laf)
923     {
924     case "crossplatform":
925       lafSet = setCrossPlatformLookAndFeel();
926       if (!lafSet)
927       {
928         Console.error("Could not set requested laf=" + laf);
929       }
930       break;
931     case "system":
932       lafSet = setSystemLookAndFeel();
933       if (!lafSet)
934       {
935         Console.error("Could not set requested laf=" + laf);
936       }
937       break;
938     case "gtk":
939       lafSet = setGtkLookAndFeel();
940       if (!lafSet)
941       {
942         Console.error("Could not set requested laf=" + laf);
943       }
944       break;
945     case "metal":
946       lafSet = setMetalLookAndFeel();
947       if (!lafSet)
948       {
949         Console.error("Could not set requested laf=" + laf);
950       }
951       break;
952     case "nimbus":
953       lafSet = setNimbusLookAndFeel();
954       if (!lafSet)
955       {
956         Console.error("Could not set requested laf=" + laf);
957       }
958       break;
959     case "quaqua":
960       lafSet = setQuaquaLookAndFeel();
961       if (!lafSet)
962       {
963         Console.error("Could not set requested laf=" + laf);
964       }
965       break;
966     case "vaqua":
967       lafSet = setVaquaLookAndFeel();
968       if (!lafSet)
969       {
970         Console.error("Could not set requested laf=" + laf);
971       }
972       break;
973     case "mac":
974       lafSet = setMacLookAndFeel();
975       if (!lafSet)
976       {
977         Console.error("Could not set requested laf=" + laf);
978       }
979       break;
980     case "none":
981       break;
982     default:
983       Console.error("Requested laf=" + laf + " not implemented");
984     }
985     if (!lafSet)
986     {
987       setSystemLookAndFeel();
988       if (Platform.isLinux())
989       {
990         setMetalLookAndFeel();
991       }
992       if (Platform.isMac())
993       {
994         setMacLookAndFeel();
995       }
996     }
997   }
998
999   private static boolean setCrossPlatformLookAndFeel()
1000   {
1001     boolean set = false;
1002     try
1003     {
1004       UIManager.setLookAndFeel(
1005               UIManager.getCrossPlatformLookAndFeelClassName());
1006       set = true;
1007     } catch (Exception ex)
1008     {
1009       Console.error("Unexpected Look and Feel Exception");
1010       Console.error(ex.getMessage());
1011       Console.debug(Cache.getStackTraceString(ex));
1012     }
1013     return set;
1014   }
1015
1016   private static boolean setSystemLookAndFeel()
1017   {
1018     boolean set = false;
1019     try
1020     {
1021       UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
1022       set = true;
1023     } catch (Exception ex)
1024     {
1025       Console.error("Unexpected Look and Feel Exception");
1026       Console.error(ex.getMessage());
1027       Console.debug(Cache.getStackTraceString(ex));
1028     }
1029     return set;
1030   }
1031
1032   private static boolean setSpecificLookAndFeel(String name,
1033           String className, boolean nameStartsWith)
1034   {
1035     boolean set = false;
1036     try
1037     {
1038       for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels())
1039       {
1040         if (info.getName() != null && nameStartsWith
1041                 ? info.getName().toLowerCase(Locale.ROOT)
1042                         .startsWith(name.toLowerCase(Locale.ROOT))
1043                 : info.getName().toLowerCase(Locale.ROOT)
1044                         .equals(name.toLowerCase(Locale.ROOT)))
1045         {
1046           className = info.getClassName();
1047           break;
1048         }
1049       }
1050       UIManager.setLookAndFeel(className);
1051       set = true;
1052     } catch (Exception ex)
1053     {
1054       Console.error("Unexpected Look and Feel Exception");
1055       Console.error(ex.getMessage());
1056       Console.debug(Cache.getStackTraceString(ex));
1057     }
1058     return set;
1059   }
1060
1061   private static boolean setGtkLookAndFeel()
1062   {
1063     return setSpecificLookAndFeel("gtk",
1064             "com.sun.java.swing.plaf.gtk.GTKLookAndFeel", true);
1065   }
1066
1067   private static boolean setMetalLookAndFeel()
1068   {
1069     return setSpecificLookAndFeel("metal",
1070             "javax.swing.plaf.metal.MetalLookAndFeel", false);
1071   }
1072
1073   private static boolean setNimbusLookAndFeel()
1074   {
1075     return setSpecificLookAndFeel("nimbus",
1076             "javax.swing.plaf.nimbus.NimbusLookAndFeel", false);
1077   }
1078
1079   private static boolean setQuaquaLookAndFeel()
1080   {
1081     return setSpecificLookAndFeel("quaqua",
1082             ch.randelshofer.quaqua.QuaquaManager.getLookAndFeel().getClass()
1083                     .getName(),
1084             false);
1085   }
1086
1087   private static boolean setVaquaLookAndFeel()
1088   {
1089     return setSpecificLookAndFeel("vaqua",
1090             "org.violetlib.aqua.AquaLookAndFeel", false);
1091   }
1092
1093   private static boolean setMacLookAndFeel()
1094   {
1095     boolean set = false;
1096     System.setProperty("com.apple.mrj.application.apple.menu.about.name",
1097             ChannelProperties.getProperty("app_name"));
1098     System.setProperty("apple.laf.useScreenMenuBar", "true");
1099     set = setQuaquaLookAndFeel();
1100     if ((!set) || !UIManager.getLookAndFeel().getClass().toString()
1101             .toLowerCase(Locale.ROOT).contains("quaqua"))
1102     {
1103       set = setVaquaLookAndFeel();
1104     }
1105     return set;
1106   }
1107
1108   private static void showUsage()
1109   {
1110     System.out.println(
1111             "Usage: jalview -open [FILE] [OUTPUT_FORMAT] [OUTPUT_FILE]\n\n"
1112                     + "-nodisplay\tRun Jalview without User Interface.\n"
1113                     + "-props FILE\tUse the given Jalview properties file instead of users default.\n"
1114                     + "-colour COLOURSCHEME\tThe colourscheme to be applied to the alignment\n"
1115                     + "-annotations FILE\tAdd precalculated annotations to the alignment.\n"
1116                     + "-tree FILE\tLoad the given newick format tree file onto the alignment\n"
1117                     + "-features FILE\tUse the given file to mark features on the alignment.\n"
1118                     + "-fasta FILE\tCreate alignment file FILE in Fasta format.\n"
1119                     + "-clustal FILE\tCreate alignment file FILE in Clustal format.\n"
1120                     + "-pfam FILE\tCreate alignment file FILE in PFAM format.\n"
1121                     + "-msf FILE\tCreate alignment file FILE in MSF format.\n"
1122                     + "-pileup FILE\tCreate alignment file FILE in Pileup format\n"
1123                     + "-pir FILE\tCreate alignment file FILE in PIR format.\n"
1124                     + "-blc FILE\tCreate alignment file FILE in BLC format.\n"
1125                     + "-json FILE\tCreate alignment file FILE in JSON format.\n"
1126                     + "-jalview FILE\tCreate alignment file FILE in Jalview format.\n"
1127                     + "-png FILE\tCreate PNG image FILE from alignment.\n"
1128                     + "-svg FILE\tCreate SVG image FILE from alignment.\n"
1129                     + "-html FILE\tCreate HTML file from alignment.\n"
1130                     + "-biojsMSA FILE\tCreate BioJS MSA Viewer HTML file from alignment.\n"
1131                     + "-imgMap FILE\tCreate HTML file FILE with image map of PNG image.\n"
1132                     + "-eps FILE\tCreate EPS file FILE from alignment.\n"
1133                     + "-questionnaire URL\tQueries the given URL for information about any Jalview user questionnaires.\n"
1134                     + "-noquestionnaire\tTurn off questionnaire check.\n"
1135                     + "-nonews\tTurn off check for Jalview news.\n"
1136                     + "-nousagestats\tTurn off google analytics tracking for this session.\n"
1137                     + "-sortbytree OR -nosortbytree\tEnable or disable sorting of the given alignment by the given tree\n"
1138                     // +
1139                     // "-setprop PROPERTY=VALUE\tSet the given Jalview property,
1140                     // after all other properties files have been read\n\t
1141                     // (quote the 'PROPERTY=VALUE' pair to ensure spaces are
1142                     // passed in correctly)"
1143                     + "-jabaws URL\tSpecify URL for Jabaws services (e.g. for a local installation).\n"
1144                     + "-fetchfrom nickname\tQuery nickname for features for the alignments and display them.\n"
1145                     + "-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"
1146                     + "-jvmmempc=PERCENT\tOnly available with standalone executable jar or jalview.bin.Launcher. Limit maximum heap size (memory) to PERCENT% of total physical memory detected. This defaults to 90 if total physical memory can be detected. See https://www.jalview.org/help/html/memory.html for more details.\n"
1147                     + "-jvmmemmax=MAXMEMORY\tOnly available with standalone executable jar or jalview.bin.Launcher. Limit maximum heap size (memory) to MAXMEMORY. MAXMEMORY can be specified in bytes, kilobytes(k), megabytes(m), gigabytes(g) or if you're lucky enough, terabytes(t). This defaults to 32g if total physical memory can be detected, or to 8g if total physical memory cannot be detected. See https://www.jalview.org/help/html/memory.html for more details.\n"
1148                     + "\n~Read documentation in Application or visit https://www.jalview.org for description of Features and Annotations file~\n\n");
1149   }
1150
1151   private static void startUsageStats(final Desktop desktop)
1152   {
1153     /**
1154      * start a User Config prompt asking if we can log usage statistics.
1155      */
1156     PromptUserConfig prompter = new PromptUserConfig(Desktop.desktop,
1157             "USAGESTATS", "Jalview Usage Statistics",
1158             "Do you want to help make Jalview better by enabling "
1159                     + "the collection of usage statistics with Google Analytics ?"
1160                     + "\n\n(you can enable or disable usage tracking in the preferences)",
1161             new Runnable()
1162             {
1163               @Override
1164               public void run()
1165               {
1166                 Console.debug(
1167                         "Initialising googletracker for usage stats.");
1168                 Cache.initGoogleTracker();
1169                 Console.debug("Tracking enabled.");
1170               }
1171             }, new Runnable()
1172             {
1173               @Override
1174               public void run()
1175               {
1176                 Console.debug("Not enabling Google Tracking.");
1177               }
1178             }, null, true);
1179     desktop.addDialogThread(prompter);
1180   }
1181
1182   /**
1183    * Locate the given string as a file and pass it to the groovy interpreter.
1184    * 
1185    * @param groovyscript
1186    *          the script to execute
1187    * @param jalviewContext
1188    *          the Jalview Desktop object passed in to the groovy binding as the
1189    *          'Jalview' object.
1190    */
1191   private void executeGroovyScript(String groovyscript, AlignFrame af)
1192   {
1193     /**
1194      * for scripts contained in files
1195      */
1196     File tfile = null;
1197     /**
1198      * script's URI
1199      */
1200     URL sfile = null;
1201     if (groovyscript.trim().equals("STDIN"))
1202     {
1203       // read from stdin into a tempfile and execute it
1204       try
1205       {
1206         tfile = File.createTempFile("jalview", "groovy");
1207         PrintWriter outfile = new PrintWriter(
1208                 new OutputStreamWriter(new FileOutputStream(tfile)));
1209         BufferedReader br = new BufferedReader(
1210                 new InputStreamReader(System.in));
1211         String line = null;
1212         while ((line = br.readLine()) != null)
1213         {
1214           outfile.write(line + "\n");
1215         }
1216         br.close();
1217         outfile.flush();
1218         outfile.close();
1219
1220       } catch (Exception ex)
1221       {
1222         System.err.println("Failed to read from STDIN into tempfile "
1223                 + ((tfile == null) ? "(tempfile wasn't created)"
1224                         : tfile.toString()));
1225         ex.printStackTrace();
1226         return;
1227       }
1228       try
1229       {
1230         sfile = tfile.toURI().toURL();
1231       } catch (Exception x)
1232       {
1233         System.err.println(
1234                 "Unexpected Malformed URL Exception for temporary file created from STDIN: "
1235                         + tfile.toURI());
1236         x.printStackTrace();
1237         return;
1238       }
1239     }
1240     else
1241     {
1242       try
1243       {
1244         sfile = new URI(groovyscript).toURL();
1245       } catch (Exception x)
1246       {
1247         tfile = new File(groovyscript);
1248         if (!tfile.exists())
1249         {
1250           System.err.println("File '" + groovyscript + "' does not exist.");
1251           return;
1252         }
1253         if (!tfile.canRead())
1254         {
1255           System.err.println("File '" + groovyscript + "' cannot be read.");
1256           return;
1257         }
1258         if (tfile.length() < 1)
1259         {
1260           System.err.println("File '" + groovyscript + "' is empty.");
1261           return;
1262         }
1263         try
1264         {
1265           sfile = tfile.getAbsoluteFile().toURI().toURL();
1266         } catch (Exception ex)
1267         {
1268           System.err.println("Failed to create a file URL for "
1269                   + tfile.getAbsoluteFile());
1270           return;
1271         }
1272       }
1273     }
1274     try
1275     {
1276       Map<String, java.lang.Object> vbinding = new HashMap<>();
1277       vbinding.put("Jalview", this);
1278       if (af != null)
1279       {
1280         vbinding.put("currentAlFrame", af);
1281       }
1282       Binding gbinding = new Binding(vbinding);
1283       GroovyScriptEngine gse = new GroovyScriptEngine(new URL[] { sfile });
1284       gse.run(sfile.toString(), gbinding);
1285       if ("STDIN".equals(groovyscript))
1286       {
1287         // delete temp file that we made -
1288         // only if it was successfully executed
1289         tfile.delete();
1290       }
1291     } catch (Exception e)
1292     {
1293       System.err.println("Exception Whilst trying to execute file " + sfile
1294               + " as a groovy script.");
1295       e.printStackTrace(System.err);
1296
1297     }
1298   }
1299
1300   public static boolean isHeadlessMode()
1301   {
1302     String isheadless = System.getProperty("java.awt.headless");
1303     if (isheadless != null && isheadless.equalsIgnoreCase("true"))
1304     {
1305       return true;
1306     }
1307     return false;
1308   }
1309
1310   public AlignFrame[] getAlignFrames()
1311   {
1312     return desktop == null ? new AlignFrame[] { getCurrentAlignFrame() }
1313             : Desktop.getAlignFrames();
1314
1315   }
1316
1317   /**
1318    * Quit method delegates to Desktop.quit - unless running in headless mode
1319    * when it just ends the JVM
1320    */
1321   public void quit()
1322   {
1323     if (desktop != null)
1324     {
1325       desktop.quit();
1326     }
1327     else
1328     {
1329       System.exit(0);
1330     }
1331   }
1332
1333   public static AlignFrame getCurrentAlignFrame()
1334   {
1335     return Jalview.currentAlignFrame;
1336   }
1337
1338   public static void setCurrentAlignFrame(AlignFrame currentAlignFrame)
1339   {
1340     Jalview.currentAlignFrame = currentAlignFrame;
1341   }
1342 }