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