JAL-1503 update version in GPL header
[jalview.git] / src / jalview / bin / Cache.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.1)
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 jalview.ws.dbsources.das.api.DasSourceRegistryI;
22 import jalview.ws.dbsources.das.datamodel.DasSourceRegistry;
23
24 import java.awt.Color;
25 import java.io.*;
26 import java.text.DateFormat;
27 import java.text.SimpleDateFormat;
28 import java.util.*;
29
30 import org.apache.log4j.*;
31
32 /**
33  * Stores and retrieves Jalview Application Properties Lists and fields within
34  * list entries are separated by '|' symbols unless otherwise stated (|) clauses
35  * are alternative values for a tag. <br>
36  * <br>
37  * Current properties include:
38  * <ul>
39  * <br>
40  * logs.Axis.Level - one of the stringified Levels for log4j controlling the
41  * logging level for axis (used for web services) <br>
42  * </li>
43  * <li>logs.Castor.Level - one of the stringified Levels for log4j controlling
44  * the logging level for castor (used for serialization) <br>
45  * </li>
46  * <li>logs.Jalview.Level - Cache.log stringified level. <br>
47  * </li>
48  * <li>SCREEN_WIDTH</li>
49  * <li>SCREEN_HEIGHT</li>
50  * <li>SCREEN_Y=285</li>
51  * <li>SCREEN_X=371</li>
52  * <li>SHOW_FULLSCREEN boolean</li>
53  * <li>FONT_NAME java font name for alignment text display</li>
54  * <li>FONT_SIZE size of displayed alignment text</li>
55  * <li>FONT_STYLE style of font displayed (sequence labels are always italic)</li>
56  * <li>GAP_SYMBOL character to treat as gap symbol (usually -,.,' ')</li>
57  * <li>LAST_DIRECTORY last directory for browsing alignment</li>
58  * <li>USER_DEFINED_COLOURS list of user defined colour scheme files</li>
59  * <li>SHOW_FULL_ID show id with '/start-end' numbers appended</li>
60  * <li>SHOW_IDENTITY show percentage identity annotation</li>
61  * <li>SHOW_QUALITY show alignment quality annotation</li>
62  * <li>SHOW_ANNOTATIONS show alignment annotation rows</li>
63  * <li>SHOW_CONSERVATION show alignment conservation annotation</li>
64  * <li>CENTRE_COLUMN_LABELS centre the labels at each column in a displayed
65  * annotation row</li>
66  * <li>DEFAULT_COLOUR default colour scheme to apply for a new alignment</li>
67  * <li>DEFAULT_FILE_FORMAT file format used to save</li>
68  * <li>STARTUP_FILE file loaded on startup (may be a fully qualified url)</li>
69  * <li>SHOW_STARTUP_FILE flag to control loading of startup file</li>
70  * <li>VERSION the version of the jalview build</li>
71  * <li>BUILD_DATE date of this build</li>
72  * <li>LATEST_VERSION the latest jalview version advertised on the
73  * www.jalview.org</li>
74  * <li>PIR_MODELLER boolean indicating if PIR files are written with MODELLER
75  * descriptions</li>
76  * <li>(FASTA,MSF,PILEUP,CLUSTAL,BLC,PIR,PFAM)_JVSUFFIX boolean for adding jv
77  * suffix to file</li>
78  * <li>RECENT_URL list of recently retrieved URLs</li>
79  * <li>RECENT_FILE list of recently opened files</li>
80  * <li>USE_PROXY flag for whether a http proxy is to be used</li>
81  * <li>PROXY_SERVER the proxy</li>
82  * <li>PROXY_PORT</li>
83  * <li>NOQUESTIONNAIRES true to prevent jalview from checking the questionnaire
84  * service</li>
85  * <li>QUESTIONNAIRE last questionnaire:responder id string from questionnaire
86  * service</li>
87  * <li>USAGESTATS (false - user prompted) Enable google analytics tracker for
88  * collecting usage statistics</li>
89  * <li>DAS_LOCAL_SOURCE list of local das sources</li>
90  * <li>SHOW_OVERVIEW boolean for overview window display</li>
91  * <li>ANTI_ALIAS boolean for smooth fonts</li>
92  * <li>RIGHT_ALIGN_IDS boolean</li>
93  * <li>AUTO_CALC_CONSENSUS boolean for automatic recalculation of consensus</li>
94  * <li>PAD_GAPS boolean</li>
95  * <li>ID_ITALICS boolean</li>
96  * <li>SHOW_JV_SUFFIX</li>
97  * <li>WRAP_ALIGNMENT</li>
98  * <li>EPS_RENDERING (Prompt each time|Lineart|Text) default for EPS rendering
99  * style check</li>
100  * <li>SORT_ALIGNMENT (No sort|Id|Pairwise Identity)</li>
101  * <li>SEQUENCE_LINKS list of name|URL pairs for opening a url with
102  * $SEQUENCE_ID$</li>
103  * <li>GROUP_LINKS list of name|URL[|&lt;separator&gt;] tuples - see
104  * jalview.utils.GroupURLLink for more info</li>
105  * <li>DAS_REGISTRY_URL the registry to query</li>
106  * <li>DEFAULT_BROWSER for unix</li>
107  * <li>DAS_ACTIVE_SOURCE list of active sources</li>
108  * <li>SHOW_MEMUSAGE boolean show memory usage and warning indicator on desktop
109  * (false)</li>
110  * <li>VERSION_CHECK (true) check for the latest release version from
111  * www.jalview.org (or the alias given by the www.jalview.org property)</li>
112  * <li>SHOW_NPFEATS_TOOLTIP (true) show non-positional features in the Sequence
113  * ID tooltip</li>
114  * <li>SHOW_DBREFS_TOOLTIP (true) show Database Cross References in the Sequence
115  * ID tooltip</li>
116  * <li>SHOW_UNCONSERVED (false) only render unconserved residues - conserved
117  * displayed as '.'</li>
118  * <li>SORT_BY_TREE (false) sort the current alignment view according to the
119  * order of a newly displayed tree</li>
120  * <li>DBFETCH_USEPICR (false) use PICR to recover valid DB references from
121  * sequence ID strings before attempting retrieval from any datasource</li>
122  * <li>SHOW_GROUP_CONSENSUS (false) Show consensus annotation for groups in the
123  * alignment.</li>
124  * <li>SHOW_GROUP_CONSERVATION (false) Show conservation annotation for groups
125  * in the alignment.</li>
126  * <li>SHOW_CONSENSUS_HISTOGRAM (false) Show consensus annotation row's
127  * histogram.</li>
128  * <li>SHOW_CONSENSUS_LOGO (false) Show consensus annotation row's sequence
129  * logo.</li>
130  * <li>NORMALISE_CONSENSUS_LOGO (false) Show consensus annotation row's sequence
131  * logo normalised to row height rather than histogram height.</li>
132  * <li>FOLLOW_SELECTIONS (true) Controls whether a new alignment view should
133  * respond to selections made in other alignments containing the same sequences.
134  * </li>
135  * <li>SHOW_WSDISCOVERY_ERRORS (true) Controls if the web service URL discovery
136  * warning dialog box is displayed.</li>
137  * <li>ANNOTATIONCOLOUR_MIN (orange) Shade used for minimum value of annotation
138  * when shading by annotation</li>
139  * <li>ANNOTATIONCOLOUR_MAX (red) Shade used for maximum value of annotation
140  * when shading by annotation</li>
141  * <li>www.jalview.org (http://www.jalview.org) a property enabling all HTTP
142  * requests to be redirected to a mirror of http://www.jalview.org</li>
143  * 
144  * <li>FIGURE_AUTOIDWIDTH (false) Expand the left hand column of an exported
145  * alignment figure to accommodate even the longest sequence ID or annotation
146  * label.</li>
147  * <li>FIGURE_FIXEDIDWIDTH Specifies the width to use for the left-hand column
148  * when exporting an alignment as a figure (setting FIGURE_AUTOIDWIDTH to true
149  * will override this).</li>
150  * <li></li>
151  * 
152  * </ul>
153  * Deprecated settings:
154  * <ul>
155  * *
156  * <li>DISCOVERY_START - Boolean - controls if discovery services are queried on
157  * startup (JWS1 services only)</li>
158  * <li>DISCOVERY_URLS - comma separated list of Discovery Service endpoints.
159  * (JWS1 services only)</li>
160  * <li>SHOW_JWS1_SERVICES (true) enable or disable the original Jalview 2
161  * services in the desktop GUI</li>
162  * <li>ENABLE_RSBS_EDITOR (false for 2.7 release) enable or disable RSBS editing
163  * panel in web service preferences</li>
164  * </ul>
165  * 
166  * @author $author$
167  * @version $Revision$
168  */
169 public class Cache
170 {
171   /**
172    * property giving log4j level for CASTOR loggers
173    */
174   public static final String CASTORLOGLEVEL = "logs.Castor.level";
175
176   /**
177    * property giving log4j level for AXIS loggers
178    */
179   public static final String AXISLOGLEVEL = "logs.Axis.level";
180
181   /**
182    * property giving log4j level for Jalview Log
183    */
184   public static final String JALVIEWLOGLEVEL = "logs.Jalview.level";
185
186   public static final String DAS_LOCAL_SOURCE = "DAS_LOCAL_SOURCE";
187
188   public static final String DAS_REGISTRY_URL = "DAS_REGISTRY_URL";
189
190   public static final String DAS_ACTIVE_SOURCE = "DAS_ACTIVE_SOURCE";
191
192   /**
193    * Initialises the Jalview Application Log
194    */
195   public static Logger log;
196
197   /** Jalview Properties */
198   public static Properties applicationProperties = new Properties();
199
200   /** Default file is ~/.jalview_properties */
201   static String propertiesFile;
202
203   private static boolean propsAreReadOnly = false;
204
205   public static void initLogger()
206   {
207     if (log != null)
208     {
209       return;
210     }
211     try
212     {
213       // TODO: redirect stdout and stderr here in order to grab the output of
214       // the log
215
216       ConsoleAppender ap = new ConsoleAppender(new SimpleLayout(),
217               "System.err");
218       ap.setName("JalviewLogger");
219       org.apache.log4j.Logger.getRootLogger().addAppender(ap); // catch all for
220       // log output
221       Logger laxis = Logger.getLogger("org.apache.axis");
222       Logger lcastor = Logger.getLogger("org.exolab.castor");
223       jalview.bin.Cache.log = Logger.getLogger("jalview.bin.Jalview");
224
225       laxis.setLevel(Level.toLevel(Cache.getDefault("logs.Axis.Level",
226               Level.INFO.toString())));
227       lcastor.setLevel(Level.toLevel(Cache.getDefault("logs.Castor.Level",
228               Level.INFO.toString())));
229       lcastor = Logger.getLogger("org.exolab.castor.xml");
230       lcastor.setLevel(Level.toLevel(Cache.getDefault("logs.Castor.Level",
231               Level.INFO.toString())));
232       // lcastor = Logger.getLogger("org.exolab.castor.xml.Marshaller");
233       // lcastor.setLevel(Level.toLevel(Cache.getDefault("logs.Castor.Level",
234       // Level.INFO.toString())));
235       jalview.bin.Cache.log.setLevel(Level.toLevel(Cache.getDefault(
236               "logs.Jalview.level", Level.INFO.toString())));
237       // laxis.addAppender(ap);
238       // lcastor.addAppender(ap);
239       // jalview.bin.Cache.log.addAppender(ap);
240       // Tell the user that debug is enabled
241       jalview.bin.Cache.log.debug("Jalview Debugging Output Follows.");
242     } catch (Exception ex)
243     {
244       System.err.println("Problems initializing the log4j system\n");
245       ex.printStackTrace(System.err);
246     }
247   }
248
249   /** Called when Jalview is started */
250   public static void loadProperties(String propsFile)
251   {
252     propertiesFile = propsFile;
253     if (propsFile == null)
254     {
255       propertiesFile = System.getProperty("user.home") + File.separatorChar
256               + ".jalview_properties";
257     }
258     else
259     {
260       // don't corrupt the file we've been given.
261       propsAreReadOnly = true;
262     }
263
264     try
265     {
266       InputStream fis;
267       try
268       {
269         fis = new java.net.URL(propertiesFile).openStream();
270         System.out.println("Loading jalview properties from : "
271                 + propertiesFile);
272         System.out
273                 .println("Disabling Jalview writing to user's local properties file.");
274         propsAreReadOnly = true;
275
276       } catch (Exception ex)
277       {
278         fis = null;
279       }
280       if (fis == null)
281       {
282         fis = new FileInputStream(propertiesFile);
283       }
284       applicationProperties.load(fis);
285       applicationProperties.remove("LATEST_VERSION");
286       applicationProperties.remove("VERSION");
287       fis.close();
288     } catch (Exception ex)
289     {
290       System.out.println("Error reading properties file: " + ex);
291     }
292
293     if (getDefault("USE_PROXY", false))
294     {
295       String proxyServer = getDefault("PROXY_SERVER", ""), proxyPort = getDefault(
296               "PROXY_PORT", "8080");
297
298       System.out.println("Using proxyServer: " + proxyServer
299               + " proxyPort: " + proxyPort);
300
301       System.setProperty("http.proxyHost", proxyServer);
302       System.setProperty("http.proxyPort", proxyPort);
303     }
304
305     // LOAD THE AUTHORS FROM THE authors.props file
306     try
307     {
308       String authorDetails = "jar:".concat(Cache.class
309               .getProtectionDomain().getCodeSource().getLocation()
310               .toString().concat("!/authors.props"));
311
312       java.net.URL localJarFileURL = new java.net.URL(authorDetails);
313
314       InputStream in = localJarFileURL.openStream();
315       applicationProperties.load(in);
316       in.close();
317     } catch (Exception ex)
318     {
319       System.out.println("Error reading author details: " + ex);
320       applicationProperties.remove("AUTHORS");
321       applicationProperties.remove("AUTHORFNAMES");
322       applicationProperties.remove("YEAR");
323     }
324
325     // FIND THE VERSION NUMBER AND BUILD DATE FROM jalview.jar
326     // MUST FOLLOW READING OF LOCAL PROPERTIES FILE AS THE
327     // VERSION MAY HAVE CHANGED SINCE LAST USING JALVIEW
328     try
329     {
330       String buildDetails = "jar:".concat(Cache.class.getProtectionDomain()
331               .getCodeSource().getLocation().toString()
332               .concat("!/.build_properties"));
333
334       java.net.URL localJarFileURL = new java.net.URL(buildDetails);
335
336       InputStream in = localJarFileURL.openStream();
337       applicationProperties.load(in);
338       in.close();
339     } catch (Exception ex)
340     {
341       System.out.println("Error reading build details: " + ex);
342       applicationProperties.remove("VERSION");
343     }
344
345     String jnlpVersion = System.getProperty("jalview.version");
346     String codeVersion = getProperty("VERSION");
347
348     if (codeVersion == null)
349     {
350       // THIS SHOULD ONLY BE THE CASE WHEN TESTING!!
351       codeVersion = "Test";
352       jnlpVersion = "Test";
353     }
354
355     System.out.println("Jalview Version: " + codeVersion);
356
357     // jnlpVersion will be null if we're using InstallAnywhere
358     // Dont do this check if running in headless mode
359     if (jnlpVersion == null
360             && getDefault("VERSION_CHECK", true)
361             && (System.getProperty("java.awt.headless") == null || System
362                     .getProperty("java.awt.headless").equals("false")))
363     {
364
365       class VersionChecker extends Thread
366       {
367         public void run()
368         {
369           String orgtimeout = System
370                   .getProperty("sun.net.client.defaultConnectTimeout");
371           if (orgtimeout == null)
372           {
373             orgtimeout = "30";
374             System.out.println("# INFO: Setting default net timeout to "
375                     + orgtimeout + " seconds.");
376           }
377           String jnlpVersion = null;
378           try
379           {
380             System.setProperty("sun.net.client.defaultConnectTimeout",
381                     "5000");
382             java.net.URL url = new java.net.URL(Cache.getDefault(
383                     "www.jalview.org", "http://www.jalview.org")
384                     + "/webstart/jalview.jnlp");
385             BufferedReader in = new BufferedReader(new InputStreamReader(
386                     url.openStream()));
387             String line = null;
388             while ((line = in.readLine()) != null)
389             {
390               if (line.indexOf("jalview.version") == -1)
391               {
392                 continue;
393               }
394
395               line = line.substring(line.indexOf("value=") + 7);
396               line = line.substring(0, line.lastIndexOf("\""));
397               jnlpVersion = line;
398               break;
399             }
400           } catch (Exception ex)
401           {
402             System.out
403                     .println("Non-fatal exceptions when checking version at www.jalview.org :");
404             System.out.println(ex);
405             jnlpVersion = getProperty("VERSION");
406           }
407           System.setProperty("sun.net.client.defaultConnectTimeout",
408                   orgtimeout);
409
410           setProperty("LATEST_VERSION", jnlpVersion);
411         }
412       }
413
414       VersionChecker vc = new VersionChecker();
415       vc.start();
416     }
417     else
418     {
419       if (jnlpVersion != null)
420       {
421         setProperty("LATEST_VERSION", jnlpVersion);
422       }
423       else
424       {
425         applicationProperties.remove("LATEST_VERSION");
426       }
427     }
428
429     setProperty("VERSION", codeVersion);
430
431     // LOAD USERDEFINED COLOURS
432     jalview.gui.UserDefinedColours
433             .initUserColourSchemes(getProperty("USER_DEFINED_COLOURS"));
434     jalview.io.PIRFile.useModellerOutput = Cache.getDefault("PIR_MODELLER",
435             false);
436   }
437
438   /**
439    * Gets Jalview application property of given key. Returns null if key not
440    * found
441    * 
442    * @param key
443    *          Name of property
444    * 
445    * @return Property value
446    */
447   public static String getProperty(String key)
448   {
449     return applicationProperties.getProperty(key);
450   }
451
452   /**
453    * These methods are used when checking if the saved preference is different
454    * to the default setting
455    */
456
457   public static boolean getDefault(String property, boolean def)
458   {
459     String string = getProperty(property);
460     if (string != null)
461     {
462       def = Boolean.valueOf(string).booleanValue();
463     }
464
465     return def;
466   }
467
468   /**
469    * These methods are used when checking if the saved preference is different
470    * to the default setting
471    */
472   public static String getDefault(String property, String def)
473   {
474     String string = getProperty(property);
475     if (string != null)
476     {
477       return string;
478     }
479
480     return def;
481   }
482
483   /**
484    * Stores property in the file "HOME_DIR/.jalview_properties"
485    * 
486    * @param key
487    *          Name of object
488    * @param obj
489    *          String value of property
490    * 
491    * @return String value of property
492    */
493   public static String setProperty(String key, String obj)
494   {
495
496     try
497     {
498       applicationProperties.setProperty(key, obj);
499       if (!propsAreReadOnly)
500       {
501         FileOutputStream out = new FileOutputStream(propertiesFile);
502         applicationProperties.store(out, "---JalviewX Properties File---");
503         out.close();
504       }
505     } catch (Exception ex)
506     {
507       System.out.println("Error setting property: " + key + " " + obj
508               + "\n" + ex);
509     }
510     return obj;
511   }
512
513   /**
514    * remove the specified property from the jalview properties file
515    * 
516    * @param string
517    */
518   public static void removeProperty(String string)
519   {
520     applicationProperties.remove(string);
521     saveProperties();
522   }
523
524   /**
525    * save the properties to the jalview properties path
526    */
527   public static void saveProperties()
528   {
529     if (!propsAreReadOnly)
530     {
531       try
532       {
533         FileOutputStream out = new FileOutputStream(propertiesFile);
534         applicationProperties.store(out, "---JalviewX Properties File---");
535         out.close();
536       } catch (Exception ex)
537       {
538         System.out.println("Error saving properties: " + ex);
539       }
540     }
541   }
542
543   /**
544    * internal vamsas class discovery state
545    */
546   private static int vamsasJarsArePresent = -1;
547
548   /**
549    * Searches for vamsas client classes on class path.
550    * 
551    * @return true if vamsas client is present on classpath
552    */
553   public static boolean vamsasJarsPresent()
554   {
555     if (vamsasJarsArePresent == -1)
556     {
557       try
558       {
559         if (jalview.jbgui.GDesktop.class.getClassLoader().loadClass(
560                 "uk.ac.vamsas.client.VorbaId") != null)
561         {
562           jalview.bin.Cache.log
563                   .debug("Found Vamsas Classes (uk.ac..vamsas.client.VorbaId can be loaded)");
564           vamsasJarsArePresent = 1;
565           Logger lvclient = Logger.getLogger("uk.ac.vamsas");
566           lvclient.setLevel(Level.toLevel(Cache.getDefault(
567                   "logs.Vamsas.Level", Level.INFO.toString())));
568
569           lvclient.addAppender(log.getAppender("JalviewLogger"));
570           // Tell the user that debug is enabled
571           lvclient.debug("Jalview Vamsas Client Debugging Output Follows.");
572         }
573       } catch (Exception e)
574       {
575         vamsasJarsArePresent = 0;
576         jalview.bin.Cache.log.debug("Vamsas Classes are not present");
577       }
578     }
579     return (vamsasJarsArePresent > 0);
580   }
581
582   /**
583    * internal vamsas class discovery state
584    */
585   private static int groovyJarsArePresent = -1;
586
587   /**
588    * Searches for vamsas client classes on class path.
589    * 
590    * @return true if vamsas client is present on classpath
591    */
592   public static boolean groovyJarsPresent()
593   {
594     if (groovyJarsArePresent == -1)
595     {
596       try
597       {
598         if (Cache.class.getClassLoader().loadClass(
599                 "groovy.lang.GroovyObject") != null)
600         {
601           jalview.bin.Cache.log
602                   .debug("Found Groovy (groovy.lang.GroovyObject can be loaded)");
603           groovyJarsArePresent = 1;
604           Logger lgclient = Logger.getLogger("groovy");
605           lgclient.setLevel(Level.toLevel(Cache.getDefault(
606                   "logs.Groovy.Level", Level.INFO.toString())));
607
608           lgclient.addAppender(log.getAppender("JalviewLogger"));
609           // Tell the user that debug is enabled
610           lgclient.debug("Jalview Groovy Client Debugging Output Follows.");
611         }
612       } catch (Error e)
613       {
614         groovyJarsArePresent = 0;
615         jalview.bin.Cache.log.debug("Groovy Classes are not present", e);
616       } catch (Exception e)
617       {
618         groovyJarsArePresent = 0;
619         jalview.bin.Cache.log.debug("Groovy Classes are not present");
620       }
621     }
622     return (groovyJarsArePresent > 0);
623   }
624
625   /**
626    * GA tracker object - actually JGoogleAnalyticsTracker null if tracking not
627    * enabled.
628    */
629   protected static Object tracker = null;
630
631   protected static Class trackerfocus = null;
632
633   protected static Class jgoogleanalyticstracker = null;
634
635   /**
636    * Initialise the google tracker if it is not done already.
637    */
638   public static void initGoogleTracker()
639   {
640     if (tracker == null)
641     {
642       if (jgoogleanalyticstracker == null)
643       {
644         // try to get the tracker class
645         try
646         {
647           jgoogleanalyticstracker = Cache.class
648                   .getClassLoader()
649                   .loadClass(
650                           "com.boxysystems.jgoogleanalytics.JGoogleAnalyticsTracker");
651           trackerfocus = Cache.class.getClassLoader().loadClass(
652                   "com.boxysystems.jgoogleanalytics.FocusPoint");
653         } catch (Exception e)
654         {
655           log.debug("com.boxysystems.jgoogleanalytics package is not present - tracking not enabled.");
656           tracker = null;
657           jgoogleanalyticstracker = null;
658           trackerfocus = null;
659           return;
660         }
661       }
662       // now initialise tracker
663       Exception re = null, ex = null;
664       Error err = null;
665       String vrs = "No Version Accessible";
666       try
667       {
668         // Google analytics tracking code for Library Finder
669         tracker = jgoogleanalyticstracker.getConstructor(new Class[]
670         { String.class, String.class, String.class }).newInstance(
671                 new Object[]
672                 {
673                     "Jalview Desktop",
674                     (vrs = jalview.bin.Cache.getProperty("VERSION")
675                             + "_"
676                             + jalview.bin.Cache.getDefault("BUILD_DATE",
677                                     "unknown")), "UA-9060947-1" });
678         jgoogleanalyticstracker.getMethod("trackAsynchronously",
679                 new Class[]
680                 { trackerfocus }).invoke(tracker, new Object[]
681         { trackerfocus.getConstructor(new Class[]
682         { String.class }).newInstance(new Object[]
683         { "Application Started." }) });
684       } catch (RuntimeException e)
685       {
686         re = e;
687       } catch (Exception e)
688       {
689         ex = e;
690       } catch (Error e)
691       {
692         err = e;
693       }
694       if (re != null || ex != null || err != null)
695       {
696         if (log != null)
697         {
698           if (re != null)
699             log.debug("Caught runtime exception in googletracker init:", re);
700           if (ex != null)
701             log.warn(
702                     "Failed to initialise GoogleTracker for Jalview Desktop with version "
703                             + vrs, ex);
704           if (err != null)
705             log.error(
706                     "Whilst initing GoogleTracker for Jalview Desktop version "
707                             + vrs, err);
708         }
709         else
710         {
711           if (re != null)
712           {
713             System.err
714                     .println("Debug: Caught runtime exception in googletracker init:"
715                             + vrs);
716             re.printStackTrace();
717           }
718           if (ex != null)
719           {
720             System.err
721                     .println("Warning:  Failed to initialise GoogleTracker for Jalview Desktop with version "
722                             + vrs);
723             ex.printStackTrace();
724           }
725
726           if (err != null)
727           {
728             System.err
729                     .println("ERROR: Whilst initing GoogleTracker for Jalview Desktop version "
730                             + vrs);
731             err.printStackTrace();
732           }
733         }
734       }
735       else
736       {
737         log.debug("Successfully initialised tracker.");
738       }
739     }
740   }
741
742   /**
743    * get the user's default colour if available
744    * 
745    * @param property
746    * @param defcolour
747    * @return
748    */
749   public static Color getDefaultColour(String property, Color defcolour)
750   {
751     String colprop = getProperty(property);
752     if (colprop == null)
753     {
754       return defcolour;
755     }
756     Color col = jalview.schemes.ColourSchemeProperty
757             .getAWTColorFromName(colprop);
758     if (col == null)
759     {
760       try
761       {
762         col = new jalview.schemes.UserColourScheme(colprop).findColour('A');
763       } catch (Exception ex)
764       {
765         log.warn("Couldn't parse '" + colprop + "' as a colour for "
766                 + property);
767         col = null;
768       }
769     }
770     return (col == null) ? defcolour : col;
771   }
772
773   /**
774    * store a colour as a Jalview user default property
775    * 
776    * @param property
777    * @param colour
778    */
779   public static void setColourProperty(String property, Color colour)
780   {
781     setProperty(property, jalview.util.Format.getHexString(colour));
782   }
783
784   public static final DateFormat date_format = SimpleDateFormat
785           .getDateTimeInstance();
786
787   /**
788    * store a date in a jalview property
789    * 
790    * @param string
791    * @param time
792    */
793   public static void setDateProperty(String property, Date time)
794   {
795     setProperty(property, date_format.format(time));
796   }
797
798   /**
799    * read a date stored in a jalview property
800    * 
801    * @param property
802    * @return valid date as stored by setDateProperty, or null
803    * 
804    */
805   public static Date getDateProperty(String property)
806   {
807     String val = getProperty(property);
808     if (val != null)
809     {
810       try
811       {
812         return date_format.parse(val);
813       } catch (Exception ex)
814       {
815         System.err.println("Invalid or corrupt date in property '"
816                 + property + "' : value was '" + val + "'");
817       }
818     }
819     return null;
820   }
821
822   /**
823    * get and parse a property as an integer. send any parsing problems to
824    * System.err
825    * 
826    * @param property
827    * @return null or Integer
828    */
829   public static Integer getIntegerProperty(String property)
830   {
831     String val = getProperty(property);
832     if (val != null && (val = val.trim()).length() > 0)
833     {
834       try
835       {
836         return Integer.valueOf(val);
837       } catch (NumberFormatException x)
838       {
839         System.err.println("Invalid integer in property '" + property
840                 + "' (value was '" + val + "')");
841       }
842     }
843     return null;
844   }
845
846   private static DasSourceRegistryI sourceRegistry = null;
847
848   /**
849    * initialise and ..
850    * 
851    * @return instance of the das source registry
852    */
853   public static DasSourceRegistryI getDasSourceRegistry()
854   {
855     if (sourceRegistry == null)
856     {
857       sourceRegistry = new DasSourceRegistry();
858     }
859     return sourceRegistry;
860   }
861 }