Merge branch 'releases/Release_2_11_3_Branch'
[jalview.git] / src / jalview / bin / Cache.java
index 7af926b..483b1fc 100755 (executable)
@@ -24,7 +24,9 @@ import java.awt.Color;
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
@@ -36,11 +38,14 @@ import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
 import java.util.Enumeration;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 import java.util.Properties;
 import java.util.StringTokenizer;
 import java.util.TreeSet;
@@ -49,6 +54,8 @@ import javax.swing.LookAndFeel;
 import javax.swing.UIManager;
 
 import jalview.analytics.Plausible;
+import jalview.bin.argparser.Arg;
+import jalview.bin.argparser.ArgParser;
 import jalview.datamodel.PDBEntry;
 import jalview.gui.Preferences;
 import jalview.gui.UserDefinedColours;
@@ -136,6 +143,10 @@ import jalview.ws.sifts.SiftsSettings;
  * <li>WRAP_ALIGNMENT</li>
  * <li>EPS_RENDERING (Prompt each time|Lineart|Text) default for EPS rendering
  * style check</li>
+ * <li>BITMAP_SCALE - scale factor for PNG export - default 0.0 - native
+ * resolution</li>
+ * <li>BITMAP_HEIGHT - height bound for PNG export or 0 for unbound</li>
+ * <li>BITMAP_WIDTH - width bound for PNG export or 0 for unbound</li>
  * <li>SORT_ALIGNMENT (No sort|Id|Pairwise Identity)</li>
  * <li>SEQUENCE_LINKS list of name|URL pairs for opening a url with
  * $SEQUENCE_ID$</li>
@@ -243,6 +254,9 @@ public class Cache
    */
   public static final String JALVIEWLOGLEVEL = "logs.Jalview.level";
 
+  // for tests
+  public static final String BOOTSTRAP_TEST = "BOOTSTRAP_TEST";
+
   /**
    * Sifts settings
    */
@@ -306,6 +320,24 @@ public class Cache
   // in-memory only storage of proxy password, safer to use char array
   public static char[] proxyAuthPassword = null;
 
+  /**
+   * Session properties, set by command line, try not to affect stored
+   * properties!
+   */
+  private static Map<String, String> sessionProperties = new HashMap<>();
+
+  private static boolean bypassSessionProperties = false;
+
+  public static void enableSessionProperties()
+  {
+    bypassSessionProperties = false;
+  }
+
+  public static void disableSessionProperties()
+  {
+    bypassSessionProperties = true;
+  }
+
   /** Jalview Properties */
   public static Properties applicationProperties = new Properties()
   {
@@ -323,8 +355,20 @@ public class Cache
   /** Default file is ~/.jalview_properties */
   static String propertiesFile;
 
+  private static final String fallbackPropertiesFile = ".jalview_properties";
+
   private static boolean propsAreReadOnly = Platform.isJS();
 
+  public static boolean isPropsAreReadOnly()
+  {
+    return propsAreReadOnly;
+  }
+
+  public static void setPropsAreReadOnly(boolean propsAreReadOnly)
+  {
+    Cache.propsAreReadOnly = propsAreReadOnly;
+  }
+
   private final static String JS_PROPERTY_PREFIX = "jalview_";
 
   /**
@@ -340,7 +384,7 @@ public class Cache
     {
       String channelPrefsFilename = ChannelProperties
               .getProperty("preferences.filename");
-      String releasePrefsFilename = ".jalview_properties";
+      String releasePrefsFilename = fallbackPropertiesFile;
       propertiesFile = System.getProperty("user.home") + File.separatorChar
               + channelPrefsFilename;
       releasePropertiesFile = System.getProperty("user.home")
@@ -367,10 +411,13 @@ public class Cache
         {
           // props file provided as URL
           fis = new URL(propertiesFile).openStream();
-          System.out.println(
-                  "Loading jalview properties from : " + propertiesFile);
-          System.out.println(
-                  "Disabling Jalview writing to user's local properties file.");
+          if (!Jalview.quiet())
+          {
+            jalview.bin.Console.outPrintln(
+                    "Loading jalview properties from : " + propertiesFile);
+            jalview.bin.Console.outPrintln(
+                    "Disabling Jalview writing to user's local properties file.");
+          }
           propsAreReadOnly = true;
         } catch (Exception ex)
         {
@@ -398,7 +445,9 @@ public class Cache
         fis.close();
       } catch (Exception ex)
       {
-        System.out.println("Error reading properties file: " + ex);
+        if (!Jalview.quiet())
+          jalview.bin.Console
+                  .outPrintln("Error reading properties file: " + ex);
       }
     }
 
@@ -454,7 +503,9 @@ public class Cache
       }
     } catch (Exception ex)
     {
-      System.out.println("Error reading author details: " + ex);
+      if (!Jalview.quiet())
+        jalview.bin.Console
+                .outPrintln("Error reading author details: " + ex);
       authorDetails = null;
     }
     if (authorDetails == null)
@@ -517,8 +568,8 @@ public class Cache
           if (orgtimeout == null)
           {
             orgtimeout = "30";
-            System.out.println("# INFO: Setting default net timeout to "
-                    + orgtimeout + " seconds.");
+            Console.debug("Setting default net timeout to " + orgtimeout
+                    + " seconds.");
           }
           String remoteVersion = null;
           if (remoteBuildPropertiesUrl.startsWith("http"))
@@ -538,10 +589,13 @@ public class Cache
               remoteVersion = remoteBuildProperties.getProperty("VERSION");
             } catch (Exception ex)
             {
-              System.out.println(
-                      "Non-fatal exception when checking version at "
-                              + remoteBuildPropertiesUrl + ":");
-              System.out.println(ex);
+              if (!Jalview.quiet())
+              {
+                jalview.bin.Console.errPrintln(
+                        "Non-fatal exception when checking version at "
+                                + remoteBuildPropertiesUrl + ":");
+                jalview.bin.Console.printStackTrace(ex);
+              }
               remoteVersion = getProperty("VERSION");
             }
           }
@@ -590,8 +644,8 @@ public class Cache
         url = Cache.class.getResource(resourcePath).toString();
       } catch (Exception ex)
       {
-        System.err.println("Failed to resolve resource " + resourcePath
-                + ": " + ex.getMessage());
+        jalview.bin.Console.errPrintln("Failed to resolve resource "
+                + resourcePath + ": " + ex.getMessage());
       }
     }
     else
@@ -640,7 +694,9 @@ public class Cache
       }
     } catch (Exception ex)
     {
-      System.out.println("Error reading build details: " + ex);
+      if (!Jalview.quiet())
+        jalview.bin.Console
+                .outPrintln("Error reading build details: " + ex);
       applicationProperties.remove("VERSION");
     }
     String codeVersion = getProperty("VERSION");
@@ -660,8 +716,9 @@ public class Cache
     new BuildDetails(codeVersion, null, codeInstallation);
     if (printVersion && reportVersion)
     {
-      System.out.println(ChannelProperties.getProperty("app_name")
-              + " Version: " + codeVersion + codeInstallation);
+      jalview.bin.Console
+              .outPrintln(ChannelProperties.getProperty("app_name")
+                      + " version: " + codeVersion + codeInstallation);
     }
   }
 
@@ -687,7 +744,21 @@ public class Cache
    */
   public static String getProperty(String key)
   {
-    String prop = applicationProperties.getProperty(key);
+    return getProperty(key, false);
+  }
+
+  public static String getProperty(String key,
+          boolean skipSessionProperties)
+  {
+    String prop = null;
+    if (!(skipSessionProperties || bypassSessionProperties))
+    {
+      prop = getSessionProperty(key);
+    }
+    if (prop == null)
+    {
+      prop = applicationProperties.getProperty(key);
+    }
     if (prop == null && Platform.isJS())
     {
       prop = applicationProperties.getProperty(Platform.getUniqueAppletID()
@@ -722,8 +793,28 @@ public class Cache
         def = Integer.parseInt(string);
       } catch (NumberFormatException e)
       {
-        System.out.println("Error parsing int property '" + property
-                + "' with value '" + string + "'");
+        if (!Jalview.quiet())
+          jalview.bin.Console.errPrintln("Error parsing int property '"
+                  + property + "' with value '" + string + "'");
+      }
+    }
+
+    return def;
+  }
+
+  public static float getDefault(String property, float def)
+  {
+    String string = getProperty(property);
+    if (string != null)
+    {
+      try
+      {
+        def = Float.parseFloat(string);
+      } catch (NumberFormatException e)
+      {
+        if (!Jalview.quiet())
+          jalview.bin.Console.errPrintln("Error parsing float property '"
+                  + property + "' with value '" + string + "'");
       }
     }
 
@@ -756,16 +847,25 @@ public class Cache
     try
     {
       oldValue = applicationProperties.setProperty(key, obj);
-      if (propertiesFile != null && !propsAreReadOnly)
+      if (propertiesFile != null && !propsAreReadOnly
+      // don't rewrite if new value is same as old value
+              && !((obj == null && oldValue == null)
+                      || (obj != null && obj.equals(oldValue))))
       {
+        // reset the session property too
+        if (sessionProperties.containsKey(key))
+        {
+          sessionProperties.remove(key);
+        }
         FileOutputStream out = new FileOutputStream(propertiesFile);
         applicationProperties.store(out, "---JalviewX Properties File---");
         out.close();
       }
     } catch (Exception ex)
     {
-      System.out.println(
-              "Error setting property: " + key + " " + obj + "\n" + ex);
+      if (!Jalview.quiet())
+        jalview.bin.Console.errPrintln(
+                "Error setting property: " + key + " " + obj + "\n" + ex);
     }
     return oldValue;
   }
@@ -795,7 +895,8 @@ public class Cache
         out.close();
       } catch (Exception ex)
       {
-        System.out.println("Error saving properties: " + ex);
+        if (!Jalview.quiet())
+          jalview.bin.Console.errPrintln("Error saving properties: " + ex);
       }
     }
   }
@@ -985,8 +1086,9 @@ public class Cache
         return date_format.parse(val);
       } catch (Exception ex)
       {
-        System.err.println("Invalid or corrupt date in property '"
-                + propertyName + "' : value was '" + val + "'");
+        jalview.bin.Console
+                .errPrintln("Invalid or corrupt date in property '"
+                        + propertyName + "' : value was '" + val + "'");
       }
     }
     return null;
@@ -1009,8 +1111,8 @@ public class Cache
         return Integer.valueOf(val);
       } catch (NumberFormatException x)
       {
-        System.err.println("Invalid integer in property '" + property
-                + "' (value was '" + val + "')");
+        jalview.bin.Console.errPrintln("Invalid integer in property '"
+                + property + "' (value was '" + val + "')");
       }
     }
     return null;
@@ -1073,7 +1175,9 @@ public class Cache
         }
       } catch (Exception ex)
       {
-        System.out.println("Error loading User ColourFile\n" + ex);
+        if (!Jalview.quiet())
+          jalview.bin.Console
+                  .outPrintln("Error loading User ColourFile\n" + ex);
       }
     }
     if (!files.equals(coloursFound.toString()))
@@ -1133,6 +1237,7 @@ public class Cache
     sb.append("Java version: ");
     sb.append(System.getProperty("java.version"));
     sb.append("\n");
+    sb.append("Java platform: ");
     sb.append(System.getProperty("os.arch"));
     sb.append(" ");
     sb.append(System.getProperty("os.name"));
@@ -1153,17 +1258,19 @@ public class Cache
     sb.append(" (");
     sb.append(lafClass);
     sb.append(")\n");
+    appendIfNotNull(sb, "Channel: ",
+            ChannelProperties.getProperty("channel"), "\n", null);
     if (Console.isDebugEnabled()
             || !"release".equals(ChannelProperties.getProperty("channel")))
     {
-      appendIfNotNull(sb, "Channel: ",
-              ChannelProperties.getProperty("channel"), "\n", null);
       appendIfNotNull(sb, "Getdown appdir: ",
               System.getProperty("getdowninstanceappdir"), "\n", null);
       appendIfNotNull(sb, "Getdown appbase: ",
               System.getProperty("getdowninstanceappbase"), "\n", null);
       appendIfNotNull(sb, "Java home: ", System.getProperty("java.home"),
               "\n", "unknown");
+      appendIfNotNull(sb, "Preferences file: ", propertiesFile, "\n",
+              "unknown");
     }
     return sb.toString();
   }
@@ -1544,4 +1651,93 @@ public class Cache
     String appbase = getGetdownAppbase();
     return appbase + "/" + getdownDistDir + "/build_properties";
   }
+
+  private static final Collection<String> bootstrapProperties = new ArrayList<>(
+          Arrays.asList(JALVIEWLOGLEVEL, BOOTSTRAP_TEST));
+
+  public static Properties bootstrapProperties(String filename)
+  {
+    Properties bootstrapProps = new Properties();
+    File file = null;
+    if (filename != null)
+    {
+      file = new File(filename);
+    }
+    if (file == null || !file.exists())
+    {
+      if (file != null)
+      {
+        jalview.bin.Console
+                .errPrintln("Could not load bootstrap preferences file '"
+                        + file.getPath() + "'");
+      }
+      String channelPrefsFilename = ChannelProperties
+              .getProperty("preferences.filename");
+      String propertiesFilename = System.getProperty("user.home")
+              + File.separatorChar + channelPrefsFilename;
+      jalview.bin.Console.errPrintln(
+              "Using default properties file '" + propertiesFilename + "'");
+      file = new File(propertiesFilename);
+    }
+    if (file == null || !file.exists())
+
+    {
+      String releasePrefsFilename = fallbackPropertiesFile;
+      String releasePropertiesFilename = System.getProperty("user.home")
+              + File.separatorChar + releasePrefsFilename;
+      jalview.bin.Console.errPrintln("Falling back to properties file '"
+              + releasePropertiesFilename + "'");
+      file = new File(releasePropertiesFilename);
+    }
+
+    if (!file.exists())
+    {
+      jalview.bin.Console
+              .errPrintln("Could not load bootstrap preferences file '"
+                      + file.getPath() + "'");
+      return null;
+    }
+
+    try
+    {
+      FileInputStream in = new FileInputStream(file.getAbsoluteFile());
+      Properties props = new Properties();
+      props.load(in);
+      for (String prop : bootstrapProperties)
+      {
+        if (props.containsKey(prop))
+          bootstrapProps.put(prop, props.getProperty(prop));
+      }
+    } catch (FileNotFoundException e)
+    {
+      jalview.bin.Console
+              .errPrintln("Could not find bootstrap preferences file '"
+                      + file.getAbsolutePath() + "'");
+    } catch (IOException e)
+    {
+      jalview.bin.Console.errPrintln(
+              "IOException when loading bootstrap preferences file '"
+                      + file.getAbsolutePath() + "'");
+    }
+    return bootstrapProps;
+  }
+
+  public static void setSessionProperty(String key, String val)
+  {
+    if (key != null)
+    {
+      sessionProperties.put(key, val);
+    }
+  }
+
+  public static String getSessionProperty(String key)
+  {
+    return key == null ? null : sessionProperties.get(key);
+  }
+
+  public static boolean getArgCacheDefault(Arg a, String pref, boolean def)
+  {
+    ArgParser ap = Jalview.getInstance().getArgParser();
+    return ap.isSet(a) ? ap.getBoolean(a) : getDefault(pref, def);
+  }
 }