JAL-1519 - tidy build properties management code
[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       
288       // remove any old build properties
289       
290       deleteBuildProperties();
291       fis.close();
292     } catch (Exception ex)
293     {
294       System.out.println("Error reading properties file: " + ex);
295     }
296
297     if (getDefault("USE_PROXY", false))
298     {
299       String proxyServer = getDefault("PROXY_SERVER", ""), proxyPort = getDefault(
300               "PROXY_PORT", "8080");
301
302       System.out.println("Using proxyServer: " + proxyServer
303               + " proxyPort: " + proxyPort);
304
305       System.setProperty("http.proxyHost", proxyServer);
306       System.setProperty("http.proxyPort", proxyPort);
307     }
308
309     // LOAD THE AUTHORS FROM THE authors.props file
310     try
311     {
312       String authorDetails = "jar:".concat(Cache.class
313               .getProtectionDomain().getCodeSource().getLocation()
314               .toString().concat("!/authors.props"));
315
316       java.net.URL localJarFileURL = new java.net.URL(authorDetails);
317
318       InputStream in = localJarFileURL.openStream();
319       applicationProperties.load(in);
320       in.close();
321     } catch (Exception ex)
322     {
323       System.out.println("Error reading author details: " + ex);
324       applicationProperties.remove("AUTHORS");
325       applicationProperties.remove("AUTHORFNAMES");
326       applicationProperties.remove("YEAR");
327     }
328
329     // FIND THE VERSION NUMBER AND BUILD DATE FROM jalview.jar
330     // MUST FOLLOW READING OF LOCAL PROPERTIES FILE AS THE
331     // VERSION MAY HAVE CHANGED SINCE LAST USING JALVIEW
332     try
333     {
334       String buildDetails = "jar:".concat(Cache.class.getProtectionDomain()
335               .getCodeSource().getLocation().toString()
336               .concat("!/.build_properties"));
337
338       java.net.URL localJarFileURL = new java.net.URL(buildDetails);
339
340       InputStream in = localJarFileURL.openStream();
341       applicationProperties.load(in);
342       in.close();
343     } catch (Exception ex)
344     {
345       System.out.println("Error reading build details: " + ex);
346       applicationProperties.remove("VERSION");
347     }
348
349     String jnlpVersion = System.getProperty("jalview.version");
350     String codeVersion = getProperty("VERSION");
351
352     if (codeVersion == null)
353     {
354       // THIS SHOULD ONLY BE THE CASE WHEN TESTING!!
355       codeVersion = "Test";
356       jnlpVersion = "Test";
357     }
358
359     System.out.println("Jalview Version: " + codeVersion);
360
361     // jnlpVersion will be null if we're using InstallAnywhere
362     // Dont do this check if running in headless mode
363     if (jnlpVersion == null
364             && getDefault("VERSION_CHECK", true)
365             && (System.getProperty("java.awt.headless") == null || System
366                     .getProperty("java.awt.headless").equals("false")))
367     {
368
369       class VersionChecker extends Thread
370       {
371         public void run()
372         {
373           String orgtimeout = System
374                   .getProperty("sun.net.client.defaultConnectTimeout");
375           if (orgtimeout == null)
376           {
377             orgtimeout = "30";
378             System.out.println("# INFO: Setting default net timeout to "
379                     + orgtimeout + " seconds.");
380           }
381           String jnlpVersion = null;
382           try
383           {
384             System.setProperty("sun.net.client.defaultConnectTimeout",
385                     "5000");
386             java.net.URL url = new java.net.URL(Cache.getDefault(
387                     "www.jalview.org", "http://www.jalview.org")
388                     + "/webstart/jalview.jnlp");
389             BufferedReader in = new BufferedReader(new InputStreamReader(
390                     url.openStream()));
391             String line = null;
392             while ((line = in.readLine()) != null)
393             {
394               if (line.indexOf("jalview.version") == -1)
395               {
396                 continue;
397               }
398
399               line = line.substring(line.indexOf("value=") + 7);
400               line = line.substring(0, line.lastIndexOf("\""));
401               jnlpVersion = line;
402               break;
403             }
404           } catch (Exception ex)
405           {
406             System.out
407                     .println("Non-fatal exceptions when checking version at www.jalview.org :");
408             System.out.println(ex);
409             jnlpVersion = getProperty("VERSION");
410           }
411           System.setProperty("sun.net.client.defaultConnectTimeout",
412                   orgtimeout);
413
414           setProperty("LATEST_VERSION", jnlpVersion);
415         }
416       }
417
418       VersionChecker vc = new VersionChecker();
419       vc.start();
420     }
421     else
422     {
423       if (jnlpVersion != null)
424       {
425         setProperty("LATEST_VERSION", jnlpVersion);
426       }
427       else
428       {
429         applicationProperties.remove("LATEST_VERSION");
430       }
431     }
432
433     setProperty("VERSION", codeVersion);
434
435     // LOAD USERDEFINED COLOURS
436     jalview.gui.UserDefinedColours
437             .initUserColourSchemes(getProperty("USER_DEFINED_COLOURS"));
438     jalview.io.PIRFile.useModellerOutput = Cache.getDefault("PIR_MODELLER",
439             false);
440   }
441
442   private static void deleteBuildProperties()
443   {
444     applicationProperties.remove("LATEST_VERSION");
445     applicationProperties.remove("VERSION");
446     applicationProperties.remove("AUTHORS");
447     applicationProperties.remove("AUTHORFNAMES");
448     applicationProperties.remove("YEAR");
449   }
450
451   /**
452    * Gets Jalview application property of given key. Returns null if key not
453    * found
454    * 
455    * @param key
456    *          Name of property
457    * 
458    * @return Property value
459    */
460   public static String getProperty(String key)
461   {
462     return applicationProperties.getProperty(key);
463   }
464
465   /**
466    * These methods are used when checking if the saved preference is different
467    * to the default setting
468    */
469
470   public static boolean getDefault(String property, boolean def)
471   {
472     String string = getProperty(property);
473     if (string != null)
474     {
475       def = Boolean.valueOf(string).booleanValue();
476     }
477
478     return def;
479   }
480
481   /**
482    * These methods are used when checking if the saved preference is different
483    * to the default setting
484    */
485   public static String getDefault(String property, String def)
486   {
487     String string = getProperty(property);
488     if (string != null)
489     {
490       return string;
491     }
492
493     return def;
494   }
495
496   /**
497    * Stores property in the file "HOME_DIR/.jalview_properties"
498    * 
499    * @param key
500    *          Name of object
501    * @param obj
502    *          String value of property
503    * 
504    * @return String value of property
505    */
506   public static String setProperty(String key, String obj)
507   {
508
509     try
510     {
511       applicationProperties.setProperty(key, obj);
512       if (!propsAreReadOnly)
513       {
514         FileOutputStream out = new FileOutputStream(propertiesFile);
515         applicationProperties.store(out, "---JalviewX Properties File---");
516         out.close();
517       }
518     } catch (Exception ex)
519     {
520       System.out.println("Error setting property: " + key + " " + obj
521               + "\n" + ex);
522     }
523     return obj;
524   }
525
526   /**
527    * remove the specified property from the jalview properties file
528    * 
529    * @param string
530    */
531   public static void removeProperty(String string)
532   {
533     applicationProperties.remove(string);
534     saveProperties();
535   }
536
537   /**
538    * save the properties to the jalview properties path
539    */
540   public static void saveProperties()
541   {
542     if (!propsAreReadOnly)
543     {
544       try
545       {
546         FileOutputStream out = new FileOutputStream(propertiesFile);
547         applicationProperties.store(out, "---JalviewX Properties File---");
548         out.close();
549       } catch (Exception ex)
550       {
551         System.out.println("Error saving properties: " + ex);
552       }
553     }
554   }
555
556   /**
557    * internal vamsas class discovery state
558    */
559   private static int vamsasJarsArePresent = -1;
560
561   /**
562    * Searches for vamsas client classes on class path.
563    * 
564    * @return true if vamsas client is present on classpath
565    */
566   public static boolean vamsasJarsPresent()
567   {
568     if (vamsasJarsArePresent == -1)
569     {
570       try
571       {
572         if (jalview.jbgui.GDesktop.class.getClassLoader().loadClass(
573                 "uk.ac.vamsas.client.VorbaId") != null)
574         {
575           jalview.bin.Cache.log
576                   .debug("Found Vamsas Classes (uk.ac..vamsas.client.VorbaId can be loaded)");
577           vamsasJarsArePresent = 1;
578           Logger lvclient = Logger.getLogger("uk.ac.vamsas");
579           lvclient.setLevel(Level.toLevel(Cache.getDefault(
580                   "logs.Vamsas.Level", Level.INFO.toString())));
581
582           lvclient.addAppender(log.getAppender("JalviewLogger"));
583           // Tell the user that debug is enabled
584           lvclient.debug("Jalview Vamsas Client Debugging Output Follows.");
585         }
586       } catch (Exception e)
587       {
588         vamsasJarsArePresent = 0;
589         jalview.bin.Cache.log.debug("Vamsas Classes are not present");
590       }
591     }
592     return (vamsasJarsArePresent > 0);
593   }
594
595   /**
596    * internal vamsas class discovery state
597    */
598   private static int groovyJarsArePresent = -1;
599
600   /**
601    * Searches for vamsas client classes on class path.
602    * 
603    * @return true if vamsas client is present on classpath
604    */
605   public static boolean groovyJarsPresent()
606   {
607     if (groovyJarsArePresent == -1)
608     {
609       try
610       {
611         if (Cache.class.getClassLoader().loadClass(
612                 "groovy.lang.GroovyObject") != null)
613         {
614           jalview.bin.Cache.log
615                   .debug("Found Groovy (groovy.lang.GroovyObject can be loaded)");
616           groovyJarsArePresent = 1;
617           Logger lgclient = Logger.getLogger("groovy");
618           lgclient.setLevel(Level.toLevel(Cache.getDefault(
619                   "logs.Groovy.Level", Level.INFO.toString())));
620
621           lgclient.addAppender(log.getAppender("JalviewLogger"));
622           // Tell the user that debug is enabled
623           lgclient.debug("Jalview Groovy Client Debugging Output Follows.");
624         }
625       } catch (Error e)
626       {
627         groovyJarsArePresent = 0;
628         jalview.bin.Cache.log.debug("Groovy Classes are not present", e);
629       } catch (Exception e)
630       {
631         groovyJarsArePresent = 0;
632         jalview.bin.Cache.log.debug("Groovy Classes are not present");
633       }
634     }
635     return (groovyJarsArePresent > 0);
636   }
637
638   /**
639    * GA tracker object - actually JGoogleAnalyticsTracker null if tracking not
640    * enabled.
641    */
642   protected static Object tracker = null;
643
644   protected static Class trackerfocus = null;
645
646   protected static Class jgoogleanalyticstracker = null;
647
648   /**
649    * Initialise the google tracker if it is not done already.
650    */
651   public static void initGoogleTracker()
652   {
653     if (tracker == null)
654     {
655       if (jgoogleanalyticstracker == null)
656       {
657         // try to get the tracker class
658         try
659         {
660           jgoogleanalyticstracker = Cache.class
661                   .getClassLoader()
662                   .loadClass(
663                           "com.boxysystems.jgoogleanalytics.JGoogleAnalyticsTracker");
664           trackerfocus = Cache.class.getClassLoader().loadClass(
665                   "com.boxysystems.jgoogleanalytics.FocusPoint");
666         } catch (Exception e)
667         {
668           log.debug("com.boxysystems.jgoogleanalytics package is not present - tracking not enabled.");
669           tracker = null;
670           jgoogleanalyticstracker = null;
671           trackerfocus = null;
672           return;
673         }
674       }
675       // now initialise tracker
676       Exception re = null, ex = null;
677       Error err = null;
678       String vrs = "No Version Accessible";
679       try
680       {
681         // Google analytics tracking code for Library Finder
682         tracker = jgoogleanalyticstracker.getConstructor(new Class[]
683         { String.class, String.class, String.class }).newInstance(
684                 new Object[]
685                 {
686                     "Jalview Desktop",
687                     (vrs = jalview.bin.Cache.getProperty("VERSION")
688                             + "_"
689                             + jalview.bin.Cache.getDefault("BUILD_DATE",
690                                     "unknown")), "UA-9060947-1" });
691         jgoogleanalyticstracker.getMethod("trackAsynchronously",
692                 new Class[]
693                 { trackerfocus }).invoke(tracker, new Object[]
694         { trackerfocus.getConstructor(new Class[]
695         { String.class }).newInstance(new Object[]
696         { "Application Started." }) });
697       } catch (RuntimeException e)
698       {
699         re = e;
700       } catch (Exception e)
701       {
702         ex = e;
703       } catch (Error e)
704       {
705         err = e;
706       }
707       if (re != null || ex != null || err != null)
708       {
709         if (log != null)
710         {
711           if (re != null)
712             log.debug("Caught runtime exception in googletracker init:", re);
713           if (ex != null)
714             log.warn(
715                     "Failed to initialise GoogleTracker for Jalview Desktop with version "
716                             + vrs, ex);
717           if (err != null)
718             log.error(
719                     "Whilst initing GoogleTracker for Jalview Desktop version "
720                             + vrs, err);
721         }
722         else
723         {
724           if (re != null)
725           {
726             System.err
727                     .println("Debug: Caught runtime exception in googletracker init:"
728                             + vrs);
729             re.printStackTrace();
730           }
731           if (ex != null)
732           {
733             System.err
734                     .println("Warning:  Failed to initialise GoogleTracker for Jalview Desktop with version "
735                             + vrs);
736             ex.printStackTrace();
737           }
738
739           if (err != null)
740           {
741             System.err
742                     .println("ERROR: Whilst initing GoogleTracker for Jalview Desktop version "
743                             + vrs);
744             err.printStackTrace();
745           }
746         }
747       }
748       else
749       {
750         log.debug("Successfully initialised tracker.");
751       }
752     }
753   }
754
755   /**
756    * get the user's default colour if available
757    * 
758    * @param property
759    * @param defcolour
760    * @return
761    */
762   public static Color getDefaultColour(String property, Color defcolour)
763   {
764     String colprop = getProperty(property);
765     if (colprop == null)
766     {
767       return defcolour;
768     }
769     Color col = jalview.schemes.ColourSchemeProperty
770             .getAWTColorFromName(colprop);
771     if (col == null)
772     {
773       try
774       {
775         col = new jalview.schemes.UserColourScheme(colprop).findColour('A');
776       } catch (Exception ex)
777       {
778         log.warn("Couldn't parse '" + colprop + "' as a colour for "
779                 + property);
780         col = null;
781       }
782     }
783     return (col == null) ? defcolour : col;
784   }
785
786   /**
787    * store a colour as a Jalview user default property
788    * 
789    * @param property
790    * @param colour
791    */
792   public static void setColourProperty(String property, Color colour)
793   {
794     setProperty(property, jalview.util.Format.getHexString(colour));
795   }
796
797   public static final DateFormat date_format = SimpleDateFormat
798           .getDateTimeInstance();
799
800   /**
801    * store a date in a jalview property
802    * 
803    * @param string
804    * @param time
805    */
806   public static void setDateProperty(String property, Date time)
807   {
808     setProperty(property, date_format.format(time));
809   }
810
811   /**
812    * read a date stored in a jalview property
813    * 
814    * @param property
815    * @return valid date as stored by setDateProperty, or null
816    * 
817    */
818   public static Date getDateProperty(String property)
819   {
820     String val = getProperty(property);
821     if (val != null)
822     {
823       try
824       {
825         return date_format.parse(val);
826       } catch (Exception ex)
827       {
828         System.err.println("Invalid or corrupt date in property '"
829                 + property + "' : value was '" + val + "'");
830       }
831     }
832     return null;
833   }
834
835   /**
836    * get and parse a property as an integer. send any parsing problems to
837    * System.err
838    * 
839    * @param property
840    * @return null or Integer
841    */
842   public static Integer getIntegerProperty(String property)
843   {
844     String val = getProperty(property);
845     if (val != null && (val = val.trim()).length() > 0)
846     {
847       try
848       {
849         return Integer.valueOf(val);
850       } catch (NumberFormatException x)
851       {
852         System.err.println("Invalid integer in property '" + property
853                 + "' (value was '" + val + "')");
854       }
855     }
856     return null;
857   }
858
859   private static DasSourceRegistryI sourceRegistry = null;
860
861   /**
862    * initialise and ..
863    * 
864    * @return instance of the das source registry
865    */
866   public static DasSourceRegistryI getDasSourceRegistry()
867   {
868     if (sourceRegistry == null)
869     {
870       sourceRegistry = new DasSourceRegistry();
871     }
872     return sourceRegistry;
873   }
874 }