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