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