JAL-3631 Consolidated repeated code (mainly form verification). Added appdir hash...
authorBen Soares <b.soares@dundee.ac.uk>
Tue, 23 Jul 2024 16:03:43 +0000 (17:03 +0100)
committerBen Soares <b.soares@dundee.ac.uk>
Tue, 23 Jul 2024 16:03:43 +0000 (17:03 +0100)
utils/install4j/install4j10_template.install4j

index eb99e67..61a5d07 100644 (file)
     </launcher>
   </launchers>
   <installerGui autoUpdateDescriptorUrl="https://www.jalview.org/install4j/updates.xml">
+    <staticMembers>public static String getOsAppDataPath(Context context) {
+  Map&lt;String, String&gt; osAppDataPathMap = new HashMap&lt;&gt;();
+  osAppDataPathMap.put("macos", "Library/Application Support/Jalview-Desktop");
+  osAppDataPathMap.put("linux", ".local/share/jalview-desktop");
+  osAppDataPathMap.put("windows", "AppData\\Local\\Jalview-Desktop");
+  osAppDataPathMap.put("other", ".jalview-desktop");
+  String appDataPath;
+  String append;
+  if (Util.isMacOS()) {
+    appDataPath = osAppDataPathMap.get("macos");
+    append = context.getCompilerVariable("JALVIEW_APPLICATION_NAME");
+  } else if (Util.isWindows()) {
+    appDataPath = osAppDataPathMap.get("windows");
+    append = context.getCompilerVariable("APPLICATION_FOLDER");
+  } else if (Util.isLinux()) {
+    appDataPath = osAppDataPathMap.get("linux");
+    append = context.getCompilerVariable("APPLICATION_FOLDER").toLowerCase(Locale.ROOT);
+  } else {
+    appDataPath = osAppDataPathMap.get("other");
+    append = context.getCompilerVariable("APPLICATION_FOLDER").toLowerCase(Locale.ROOT);
+  }
+  return "~" + File.separator + appDataPath + File.separator + append;
+}
+
+// methods used in advanced options form validation
+public static void validateUserSpaceAdvancedOptionsForm(FormEnvironment formEnvironment) {
+    FormComponent fc_advancedOptions = formEnvironment.getFormComponentById("US_ADVANCED_OPTIONS");
+    FormComponent fc_notUsed = formEnvironment.getFormComponentById("US_NOT_USED");
+    FormComponent fc_userUpdates = formEnvironment.getFormComponentById("US_ALLOW_USER_APPDIR_UPDATES");
+    FormComponent fc_warning = formEnvironment.getFormComponentById("US_NO_UPDATES_WARNING");
+    LayoutGroup lg_advancedOptions = formEnvironment.getLayoutGroupById("US_ADVANCED_OPTIONS_GROUP");
+    
+    // get boolean status of "Enable advanced options" checkbox
+    JCheckBox jcb_advancedOptions = (JCheckBox) fc_advancedOptions.getConfigurationObject();
+    boolean advancedOptions = jcb_advancedOptions.isSelected();
+    
+    // set visibility of Advanced options layout group
+    lg_advancedOptions.setVisible(advancedOptions);
+    fc_notUsed.setVisible(!advancedOptions);
+    
+    if (!advancedOptions) {
+      return;
+    }
+    
+    JCheckBox jcb_userUpdates = (JCheckBox) fc_userUpdates.getConfigurationObject();
+    boolean userUpdates = fc_userUpdates.isEnabled() &amp;&amp; jcb_userUpdates.isSelected();
+    
+    boolean showWarning = advancedOptions &amp;&amp; (!userUpdates);
+    
+    fc_warning.setVisible(showWarning);
+}
+
+public static void validateSystemSpaceAdvancedOptionsForm(Context context, FormEnvironment formEnvironment) {
+    validateSystemSpaceAdvancedOptionsForm(context, formEnvironment, true);
+}
+
+public static boolean validateSystemSpaceAdvancedOptionsForm(Context context, FormEnvironment formEnvironment, boolean ret) {
+    FormComponent fc_advancedOptions = formEnvironment.getFormComponentById("SS_ADVANCED_OPTIONS");
+    FormComponent fc_notUsed = formEnvironment.getFormComponentById("SS_NOT_USED");
+    FormComponent fc_userUpdates = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_UPDATES");
+    FormComponent fc_installerUpdates = formEnvironment.getFormComponentById("SS_ALLOW_INSTALLER_APPDIR_UPDATES");
+    FormComponent fc_allowUserAppdirPath = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_PATH");
+    FormComponent fc_userAppdirPath = formEnvironment.getFormComponentById("SS_USER_APPDIR_PATH");
+    LayoutGroup lg_advancedGroup = formEnvironment.getLayoutGroupById("SS_ADVANCED_OPTIONS_GROUP");
+    LayoutGroup lg_setUserAppdirPath = formEnvironment.getLayoutGroupById("SS_SET_USER_APPDIR_PATH");
+    
+    
+    
+    // get boolean status of "Enable advanced options" checkbox
+    JCheckBox jcb_advancedOptions = (JCheckBox) fc_advancedOptions.getConfigurationObject();
+    boolean advancedOptions = jcb_advancedOptions.isSelected();
+    
+    // set visibility of Advanced options layout group
+    lg_advancedGroup.setVisible(advancedOptions);
+    fc_notUsed.setVisible(!advancedOptions);
+    
+    if (!advancedOptions) {
+      return ret;
+    }
+    
+    
+    
+    // get boolean status of "Allow user-space updates" checkbox
+    JCheckBox jcb_user = (JCheckBox) fc_userUpdates.getConfigurationObject();
+    boolean userUpdates = jcb_user.isSelected();
+    
+    // set enabled of customised user appdir path group
+    lg_setUserAppdirPath.setEnabled(advancedOptions &amp;&amp; userUpdates);
+    
+    // set enabled of allow installation updates checkbox
+    fc_installerUpdates.setEnabled(advancedOptions &amp;&amp; !userUpdates);
+    
+    
+    
+    // get boolean status of "Customise the user-space path" checkbox
+    JCheckBox jcb_allowUserAppdirPath = (JCheckBox) fc_allowUserAppdirPath.getConfigurationObject();
+    boolean allowUserAppdirPath = jcb_allowUserAppdirPath.isSelected();
+    
+    // set enabled of userAppdirPath text field
+    fc_userAppdirPath.setEnabled(advancedOptions &amp;&amp; allowUserAppdirPath &amp;&amp; fc_allowUserAppdirPath.isEnabled());
+    
+    // get String value of userAppdirPath text field
+    JTextField jtf_userAppdirPath = (JTextField) fc_userAppdirPath.getConfigurationObject();
+    String userAppdirPath = jtf_userAppdirPath.getText();
+    
+    
+    
+    // get boolean status of "Allow installation updates" checkbox
+    JCheckBox jcb_installer = (JCheckBox) fc_installerUpdates.getConfigurationObject();
+    boolean installerUpdates = jcb_installer.isSelected();
+
+
+
+    // should we show the No updates warning?
+    boolean showNoUpdatesWarning = advancedOptions &amp;&amp; !(userUpdates || installerUpdates);
+    FormComponent fc_noUpdatesWarning = formEnvironment.getFormComponentById("SS_NO_UPDATES_WARNING");
+    fc_noUpdatesWarning.setVisible(advancedOptions &amp;&amp; showNoUpdatesWarning);
+    
+    
+    
+    // should we show the invalid user-space path warning?
+    showInvalidUserAppdirPathWarning(context, formEnvironment);
+    
+    
+    
+    // set whether "Set defaults" button should be enabled
+    FormComponent fc_setDefaults = formEnvironment.getFormComponentById("SS_SET_DEFAULTS");
+    JButton jb_setDefaults = (JButton) fc_setDefaults.getConfigurationObject();
+    boolean enableSetDefaults = !( userUpdates &amp;&amp; !allowUserAppdirPath &amp;&amp; userAppdirPath.length() == 0 &amp;&amp; !installerUpdates );
+    jb_setDefaults.setEnabled(enableSetDefaults);
+    
+    
+    
+    return ret;
+}
+
+public static boolean isUserAppdirPathValid(Context context) { // this is only valid when form is updated
+    return isUserAppdirPathValid(context, (String) context.getVariable("userAppdirPath"));    
+}
+
+public static boolean isUserAppdirPathValid(Context context, FormComponent fc_userAppdirPath) {
+    // get String value of userAppdirPath text field
+    JTextField jtf_userAppdirPath = (JTextField) fc_userAppdirPath.getConfigurationObject();
+    String userAppdirPath = jtf_userAppdirPath.getText();
+    return isUserAppdirPathValid(context, userAppdirPath);
+}
+
+public static boolean isUserAppdirPathValid(Context context, String userAppdirPath) {
+    if (userAppdirPath == null || userAppdirPath.length() == 0) {
+      return true; // null will be switched to default
+    }
+    
+    boolean u = userAppdirPath.contains("%u");
+    boolean h = userAppdirPath.contains("%h");
+    boolean t = userAppdirPath.startsWith("~" + (String)context.getVariable("sys.fileSeparator"));
+    
+    return u || h || t;
+}
+
+public static void showInvalidUserAppdirPathWarning(Context context, FormEnvironment formEnvironment) {
+    FormComponent fc_advancedOptions = formEnvironment.getFormComponentById("SS_ADVANCED_OPTIONS");
+    FormComponent fc_userUpdates = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_UPDATES");
+    FormComponent fc_allowUserAppdirPath = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_PATH");
+    FormComponent fc_userAppdirPath = formEnvironment.getFormComponentById("SS_USER_APPDIR_PATH");
+    FormComponent fc_invalidPathWarning = formEnvironment.getFormComponentById("SS_INVALID_USER_APPDIR_PATH_WARNING");
+
+    // get boolean status of "Allow user-space updates" checkbox
+    boolean advancedOptions = ((JCheckBox) fc_advancedOptions.getConfigurationObject()).isSelected();
+
+    // get boolean status of "Allow user-space updates" checkbox
+    boolean userUpdates = ((JCheckBox) fc_userUpdates.getConfigurationObject()).isSelected();
+    
+    // get boolean status of "Customise the user-space path" checkbox
+    boolean allowUserAppdirPath = ((JCheckBox) fc_allowUserAppdirPath).isSelected();
+
+    // show/hide warning
+    boolean showInvalidPathWarning = advancedOptions &amp;&amp; userUpdates &amp;&amp; allowUserAppdirPath &amp;&amp;  !isUserAppdirPathValid(context, fc_userAppdirPath);
+    fc_invalidPathWarning.setVisible(showInvalidPathWarning);
+}
+
+// methods to get installer.appdir hash
+protected static final String XLATE = "0123456789abcdef";
+
+public static String hexlate (byte[] bytes, int count)
+{
+    if (bytes == null) {
+        return "";
+    }
+
+    count = Math.min(count, bytes.length);
+    char[] chars = new char[count*2];
+
+    for (int i = 0; i &lt; count; i++) {
+        int val = bytes[i];
+        if (val &lt; 0) {
+            val += 256;
+        }
+        chars[2*i] = XLATE.charAt(val/16);
+        chars[2*i+1] = XLATE.charAt(val%16);
+    }
+
+    return new String(chars);
+}
+
+public static String hexlate (byte[] bytes)
+{
+    return (bytes == null) ? "" : hexlate(bytes, bytes.length);
+}
+
+public static final String getFullPathToDirectoryHash(String install_app_dir)
+{
+  java.security.MessageDigest md;
+  try {
+    md = java.security.MessageDigest.getInstance("SHA-256");
+  } catch (java.security.NoSuchAlgorithmException nsae) {
+    throw new RuntimeException("JVM does not support SHA-256. Gurp!");
+  }
+  byte[] contents = install_app_dir.getBytes(java.nio.charset.StandardCharsets.UTF_8);
+  String hash = hexlate(md.digest(contents));
+  return hash.substring(0,8);
+}
+
+public static final String getCanonicalFullPathToDirectoryHash(String installerAppdir) {
+    try {
+        return getFullPathToDirectoryHash(new File(installerAppdir).getCanonicalPath());
+    }catch (IOException ioex) {
+        System.err.println("Unable to resolve '"+installerAppdir+"' as a proper path on this system.\nNot generating an installer appdir hash");
+        return "";
+    }
+}
+</staticMembers>
     <applications>
       <application id="installer" beanClass="com.install4j.runtime.beans.applications.InstallerApplication" actionElevationType="elevated" styleId="35" customIcnsFile="${compiler:JALVIEW_DIR}/${compiler:INSTALLER_MAC_ICON}" customIcoFile="${compiler:JALVIEW_DIR}/${compiler:INSTALLER_WINDOWS_ICON}">
         <serializedBean>
                 <serializedBean>
                   <property name="script">
                     <object class="com.install4j.api.beans.ScriptProperty">
-                      <property name="value" type="string">Util.isLinux() || Util.isUnixInstaller()</property>
+                      <property name="value" type="string">Util.isLinux() || Util.isUnixInstaller() || ( Util.isMacosInstaller() &amp;&amp; context.getBooleanVariable("isAdmin") )</property>
                     </object>
                   </property>
                   <property name="variableName" type="string">makeSymbolicLink</property>
@@ -446,27 +677,7 @@ return wrapperLink;
                 <serializedBean>
                   <property name="script">
                     <object class="com.install4j.api.beans.ScriptProperty">
-                      <property name="value" type="string">Map&lt;String, String&gt; osAppDataPathMap = new HashMap&lt;&gt;();
-osAppDataPathMap.put("macos", "Library/Application Support/Jalview-Desktop");
-osAppDataPathMap.put("linux", ".local/share/jalview-desktop");
-osAppDataPathMap.put("windows", "AppData\\Local\\Jalview-Desktop");
-osAppDataPathMap.put("other", ".jalview-desktop");
-String appDataPath;
-String append;
-if (Util.isMacOS()) {
-  appDataPath = osAppDataPathMap.get("macos");
-  append = context.getCompilerVariable("JALVIEW_APPLICATION_NAME");
-} else if (Util.isWindows()) {
-  appDataPath = osAppDataPathMap.get("windows");
-  append = context.getCompilerVariable("APPLICATION_FOLDER");
-} else if (Util.isLinux()) {
-  appDataPath = osAppDataPathMap.get("linux");
-  append = context.getCompilerVariable("APPLICATION_FOLDER").toLowerCase(Locale.ROOT);
-} else {
-  appDataPath = osAppDataPathMap.get("other");
-  append = context.getCompilerVariable("APPLICATION_FOLDER").toLowerCase(Locale.ROOT);
-}
-return "~" + File.separator + appDataPath + File.separator + append;</property>
+                      <property name="value" type="string">getOsAppDataPath(context)</property>
                     </object>
                   </property>
                   <property name="variableName" type="string">userDefaultAppdirBase</property>
@@ -725,36 +936,7 @@ return console.askOkCancel(message, true);
                   </property>
                   <property name="selectionScript">
                     <object class="com.install4j.api.beans.ScriptProperty">
-                      <property name="value" type="string">FormComponent fc_advancedOptions = formEnvironment.getFormComponentById("US_ADVANCED_OPTIONS");
-FormComponent fc_notUsed = formEnvironment.getFormComponentById("US_NOT_USED");
-FormComponent fc_userUpdates = formEnvironment.getFormComponentById("US_ALLOW_USER_APPDIR_UPDATES");
-FormComponent fc_warning = formEnvironment.getFormComponentById("US_NO_UPDATES_WARNING");
-LayoutGroup lg_advancedOptions = formEnvironment.getLayoutGroupById("US_ADVANCED_OPTIONS_GROUP");
-
-
-
-// get boolean status of "Enable advanced options" checkbox
-JCheckBox jcb_advancedOptions = (JCheckBox) fc_advancedOptions.getConfigurationObject();
-boolean advancedOptions = jcb_advancedOptions.isSelected();
-
-// set visibility of Advanced options layout group
-lg_advancedOptions.setVisible(advancedOptions);
-fc_notUsed.setVisible(!advancedOptions);
-
-if (!advancedOptions) {
-  return;
-}
-
-
-
-
-JCheckBox jcb_userUpdates = (JCheckBox) fc_userUpdates.getConfigurationObject();
-boolean userUpdates = fc_userUpdates.isEnabled() &amp;&amp; jcb_userUpdates.isSelected();
-
-boolean showWarning = advancedOptions &amp;&amp; (!userUpdates);
-
-fc_warning.setVisible(showWarning);
-</property>
+                      <property name="value" type="string">validateUserSpaceAdvancedOptionsForm(formEnvironment)</property>
                     </object>
                   </property>
                   <property name="variableName" type="string">advancedOptions</property>
@@ -840,25 +1022,7 @@ On ${installer:osName}, user updates will be installed under
                           <property name="initiallySelected" type="boolean" value="true" />
                           <property name="selectionScript">
                             <object class="com.install4j.api.beans.ScriptProperty">
-                              <property name="value" type="string">FormComponent fc_advancedOptions = formEnvironment.getFormComponentById("US_ADVANCED_OPTIONS");
-FormComponent fc_userUpdates = formEnvironment.getFormComponentById("US_ALLOW_USER_APPDIR_UPDATES");
-FormComponent fc_warning = formEnvironment.getFormComponentById("US_NO_UPDATES_WARNING");
-LayoutGroup lg_advancedOptions = formEnvironment.getLayoutGroupById("US_ADVANCED_OPTIONS_GROUP");
-
-JCheckBox jcb_advancedOptions = (JCheckBox) fc_advancedOptions.getConfigurationObject();
-boolean advancedOptions = jcb_advancedOptions.isSelected();
-
-lg_advancedOptions.setVisible(advancedOptions);
-if (!advancedOptions) {
-  return;
-}
-
-JCheckBox jcb_userUpdates = (JCheckBox) fc_userUpdates.getConfigurationObject();
-boolean userUpdates = fc_userUpdates.isEnabled() &amp;&amp; jcb_userUpdates.isSelected();
-
-boolean showWarning = advancedOptions &amp;&amp; (!userUpdates);
-fc_warning.setVisible(showWarning);
-</property>
+                              <property name="value" type="string">validateUserSpaceAdvancedOptionsForm(formEnvironment)</property>
                             </object>
                           </property>
                           <property name="variableName" type="string">allowUserDefaultAppdirUpdates</property>
@@ -933,83 +1097,7 @@ fc_warning.setVisible(showWarning);
                   </property>
                   <property name="selectionScript">
                     <object class="com.install4j.api.beans.ScriptProperty">
-                      <property name="value" type="string">FormComponent fc_advancedOptions = formEnvironment.getFormComponentById("SS_ADVANCED_OPTIONS");
-FormComponent fc_notUsed = formEnvironment.getFormComponentById("SS_NOT_USED");
-FormComponent fc_userUpdates = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_UPDATES");
-FormComponent fc_installerUpdates = formEnvironment.getFormComponentById("SS_ALLOW_INSTALLER_APPDIR_UPDATES");
-FormComponent fc_allowUserAppdirPath = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_PATH");
-FormComponent fc_userAppdirPath = formEnvironment.getFormComponentById("SS_USER_APPDIR_PATH");
-LayoutGroup lg_advancedGroup = formEnvironment.getLayoutGroupById("SS_ADVANCED_OPTIONS_GROUP");
-LayoutGroup lg_setUserAppdirPath = formEnvironment.getLayoutGroupById("SS_SET_USER_APPDIR_PATH");
-
-
-
-// get boolean status of "Enable advanced options" checkbox
-JCheckBox jcb_advancedOptions = (JCheckBox) fc_advancedOptions.getConfigurationObject();
-boolean advancedOptions = jcb_advancedOptions.isSelected();
-
-// set visibility of Advanced options layout group
-lg_advancedGroup.setVisible(advancedOptions);
-fc_notUsed.setVisible(!advancedOptions);
-
-if (!advancedOptions) {
-  return;
-}
-
-
-
-// get boolean status of "Allow user-space updates" checkbox
-JCheckBox jcb_user = (JCheckBox) fc_userUpdates.getConfigurationObject();
-boolean userUpdates = jcb_user.isSelected();
-
-// set enabled of customised user appdir path group
-lg_setUserAppdirPath.setEnabled(advancedOptions &amp;&amp; userUpdates);
-
-// set enabled of allow installation updates checkbox
-fc_installerUpdates.setEnabled(advancedOptions &amp;&amp; !userUpdates);
-
-
-
-// get boolean status of "Customise the user-space path" checkbox
-JCheckBox jcb_allowUserAppdirPath = (JCheckBox) fc_allowUserAppdirPath.getConfigurationObject();
-boolean allowUserAppdirPath = jcb_allowUserAppdirPath.isSelected();
-
-// set enabled of userAppdirPath text field
-fc_userAppdirPath.setEnabled(advancedOptions &amp;&amp; allowUserAppdirPath &amp;&amp; fc_allowUserAppdirPath.isEnabled());
-
-// get String value of userAppdirPath text field
-JTextField jtf_userAppdirPath = (JTextField) fc_userAppdirPath.getConfigurationObject();
-String userAppdirPath = jtf_userAppdirPath.getText();
-
-
-
-// get boolean status of "Allow installation updates" checkbox
-JCheckBox jcb_installer = (JCheckBox) fc_installerUpdates.getConfigurationObject();
-boolean installerUpdates = jcb_installer.isSelected();
-
-// should we show the No updates warning?
-boolean showNoUpdatesWarning = advancedOptions &amp;&amp; !(userUpdates || installerUpdates);
-FormComponent fc_noUpdatesWarning = formEnvironment.getFormComponentById("SS_NO_UPDATES_WARNING");
-fc_noUpdatesWarning.setVisible(advancedOptions &amp;&amp; showNoUpdatesWarning);
-
-
-
-// should we show the invalid user-space path warning?
-boolean u = userAppdirPath.contains("%u");
-boolean h = userAppdirPath.contains("%h");
-boolean t = userAppdirPath.startsWith("~" + (String)context.getVariable("sys.fileSeparator"));
-
-boolean showInvalidPathWarning = !( userAppdirPath.length() == 0 || u || h || t );
-FormComponent fc_invalidPathWarning = formEnvironment.getFormComponentById("SS_INVALID_USER_APPDIR_PATH_WARNING");
-fc_invalidPathWarning.setVisible(advancedOptions &amp;&amp; userUpdates &amp;&amp; allowUserAppdirPath &amp;&amp; fc_userAppdirPath.isEnabled() &amp;&amp; showInvalidPathWarning);
-
-
-
-// set whether "Set defaults" button should be enabled
-FormComponent fc_setDefaults = formEnvironment.getFormComponentById("SS_SET_DEFAULTS");
-JButton jb_setDefaults = (JButton) fc_setDefaults.getConfigurationObject();
-boolean enableSetDefaults = !( userUpdates &amp;&amp; !allowUserAppdirPath &amp;&amp; userAppdirPath.length() == 0 &amp;&amp; !installerUpdates );
-jb_setDefaults.setEnabled(enableSetDefaults);</property>
+                      <property name="value" type="string">validateSystemSpaceAdvancedOptionsForm(context, formEnvironment)</property>
                     </object>
                   </property>
                   <property name="variableName" type="string">advancedOptions</property>
@@ -1096,83 +1184,7 @@ unless customised below.
 &lt;/html&gt;</property>
                           <property name="selectionScript">
                             <object class="com.install4j.api.beans.ScriptProperty">
-                              <property name="value" type="string">FormComponent fc_advancedOptions = formEnvironment.getFormComponentById("SS_ADVANCED_OPTIONS");
-FormComponent fc_notUsed = formEnvironment.getFormComponentById("SS_NOT_USED");
-FormComponent fc_userUpdates = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_UPDATES");
-FormComponent fc_installerUpdates = formEnvironment.getFormComponentById("SS_ALLOW_INSTALLER_APPDIR_UPDATES");
-FormComponent fc_allowUserAppdirPath = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_PATH");
-FormComponent fc_userAppdirPath = formEnvironment.getFormComponentById("SS_USER_APPDIR_PATH");
-LayoutGroup lg_advancedGroup = formEnvironment.getLayoutGroupById("SS_ADVANCED_OPTIONS_GROUP");
-LayoutGroup lg_setUserAppdirPath = formEnvironment.getLayoutGroupById("SS_SET_USER_APPDIR_PATH");
-
-
-
-// get boolean status of "Enable advanced options" checkbox
-JCheckBox jcb_advancedOptions = (JCheckBox) fc_advancedOptions.getConfigurationObject();
-boolean advancedOptions = jcb_advancedOptions.isSelected();
-
-// set visibility of Advanced options layout group
-lg_advancedGroup.setVisible(advancedOptions);
-fc_notUsed.setVisible(!advancedOptions);
-
-if (!advancedOptions) {
-  return;
-}
-
-
-
-// get boolean status of "Allow user-space updates" checkbox
-JCheckBox jcb_user = (JCheckBox) fc_userUpdates.getConfigurationObject();
-boolean userUpdates = jcb_user.isSelected();
-
-// set enabled of customised user appdir path group
-lg_setUserAppdirPath.setEnabled(advancedOptions &amp;&amp; userUpdates);
-
-// set enabled of allow installation updates checkbox
-fc_installerUpdates.setEnabled(advancedOptions &amp;&amp; !userUpdates);
-
-
-
-// get boolean status of "Customise the user-space path" checkbox
-JCheckBox jcb_allowUserAppdirPath = (JCheckBox) fc_allowUserAppdirPath.getConfigurationObject();
-boolean allowUserAppdirPath = jcb_allowUserAppdirPath.isSelected();
-
-// set enabled of userAppdirPath text field
-fc_userAppdirPath.setEnabled(advancedOptions &amp;&amp; allowUserAppdirPath &amp;&amp; fc_allowUserAppdirPath.isEnabled());
-
-// get String value of userAppdirPath text field
-JTextField jtf_userAppdirPath = (JTextField) fc_userAppdirPath.getConfigurationObject();
-String userAppdirPath = jtf_userAppdirPath.getText();
-
-
-
-// get boolean status of "Allow installation updates" checkbox
-JCheckBox jcb_installer = (JCheckBox) fc_installerUpdates.getConfigurationObject();
-boolean installerUpdates = jcb_installer.isSelected();
-
-// should we show the No updates warning?
-boolean showNoUpdatesWarning = advancedOptions &amp;&amp; !(userUpdates || installerUpdates);
-FormComponent fc_noUpdatesWarning = formEnvironment.getFormComponentById("SS_NO_UPDATES_WARNING");
-fc_noUpdatesWarning.setVisible(advancedOptions &amp;&amp; showNoUpdatesWarning);
-
-
-
-// should we show the invalid user-space path warning?
-boolean u = userAppdirPath.contains("%u");
-boolean h = userAppdirPath.contains("%h");
-boolean t = userAppdirPath.startsWith("~" + (String)context.getVariable("sys.fileSeparator"));
-
-boolean showInvalidPathWarning = !( userAppdirPath.length() == 0 || u || h || t );
-FormComponent fc_invalidPathWarning = formEnvironment.getFormComponentById("SS_INVALID_USER_APPDIR_PATH_WARNING");
-fc_invalidPathWarning.setVisible(advancedOptions &amp;&amp; userUpdates &amp;&amp; allowUserAppdirPath &amp;&amp; fc_userAppdirPath.isEnabled() &amp;&amp; showInvalidPathWarning);
-
-
-
-// set whether "Set defaults" button should be enabled
-FormComponent fc_setDefaults = formEnvironment.getFormComponentById("SS_SET_DEFAULTS");
-JButton jb_setDefaults = (JButton) fc_setDefaults.getConfigurationObject();
-boolean enableSetDefaults = !( userUpdates &amp;&amp; !allowUserAppdirPath &amp;&amp; userAppdirPath.length() == 0 &amp;&amp; !installerUpdates );
-jb_setDefaults.setEnabled(enableSetDefaults);</property>
+                              <property name="value" type="string">validateSystemSpaceAdvancedOptionsForm(context, formEnvironment)</property>
                             </object>
                           </property>
                           <property name="variableName" type="string">allowUserDefaultAppdirUpdates</property>
@@ -1200,83 +1212,7 @@ jb_setDefaults.setEnabled(enableSetDefaults);</property>
                               <property name="checkboxText" type="string">Customise the user-space path</property>
                               <property name="selectionScript">
                                 <object class="com.install4j.api.beans.ScriptProperty">
-                                  <property name="value" type="string">FormComponent fc_advancedOptions = formEnvironment.getFormComponentById("SS_ADVANCED_OPTIONS");
-FormComponent fc_notUsed = formEnvironment.getFormComponentById("SS_NOT_USED");
-FormComponent fc_userUpdates = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_UPDATES");
-FormComponent fc_installerUpdates = formEnvironment.getFormComponentById("SS_ALLOW_INSTALLER_APPDIR_UPDATES");
-FormComponent fc_allowUserAppdirPath = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_PATH");
-FormComponent fc_userAppdirPath = formEnvironment.getFormComponentById("SS_USER_APPDIR_PATH");
-LayoutGroup lg_advancedGroup = formEnvironment.getLayoutGroupById("SS_ADVANCED_OPTIONS_GROUP");
-LayoutGroup lg_setUserAppdirPath = formEnvironment.getLayoutGroupById("SS_SET_USER_APPDIR_PATH");
-
-
-
-// get boolean status of "Enable advanced options" checkbox
-JCheckBox jcb_advancedOptions = (JCheckBox) fc_advancedOptions.getConfigurationObject();
-boolean advancedOptions = jcb_advancedOptions.isSelected();
-
-// set visibility of Advanced options layout group
-lg_advancedGroup.setVisible(advancedOptions);
-fc_notUsed.setVisible(!advancedOptions);
-
-if (!advancedOptions) {
-  return;
-}
-
-
-
-// get boolean status of "Allow user-space updates" checkbox
-JCheckBox jcb_user = (JCheckBox) fc_userUpdates.getConfigurationObject();
-boolean userUpdates = jcb_user.isSelected();
-
-// set enabled of customised user appdir path group
-lg_setUserAppdirPath.setEnabled(advancedOptions &amp;&amp; userUpdates);
-
-// set enabled of allow installation updates checkbox
-fc_installerUpdates.setEnabled(advancedOptions &amp;&amp; !userUpdates);
-
-
-
-// get boolean status of "Customise the user-space path" checkbox
-JCheckBox jcb_allowUserAppdirPath = (JCheckBox) fc_allowUserAppdirPath.getConfigurationObject();
-boolean allowUserAppdirPath = jcb_allowUserAppdirPath.isSelected();
-
-// set enabled of userAppdirPath text field
-fc_userAppdirPath.setEnabled(advancedOptions &amp;&amp; allowUserAppdirPath &amp;&amp; fc_allowUserAppdirPath.isEnabled());
-
-// get String value of userAppdirPath text field
-JTextField jtf_userAppdirPath = (JTextField) fc_userAppdirPath.getConfigurationObject();
-String userAppdirPath = jtf_userAppdirPath.getText();
-
-
-
-// get boolean status of "Allow installation updates" checkbox
-JCheckBox jcb_installer = (JCheckBox) fc_installerUpdates.getConfigurationObject();
-boolean installerUpdates = jcb_installer.isSelected();
-
-// should we show the No updates warning?
-boolean showNoUpdatesWarning = advancedOptions &amp;&amp; !(userUpdates || installerUpdates);
-FormComponent fc_noUpdatesWarning = formEnvironment.getFormComponentById("SS_NO_UPDATES_WARNING");
-fc_noUpdatesWarning.setVisible(advancedOptions &amp;&amp; showNoUpdatesWarning);
-
-
-
-// should we show the invalid user-space path warning?
-boolean u = userAppdirPath.contains("%u");
-boolean h = userAppdirPath.contains("%h");
-boolean t = userAppdirPath.startsWith("~" + (String)context.getVariable("sys.fileSeparator"));
-
-boolean showInvalidPathWarning = !( userAppdirPath.length() == 0 || u || h || t );
-FormComponent fc_invalidPathWarning = formEnvironment.getFormComponentById("SS_INVALID_USER_APPDIR_PATH_WARNING");
-fc_invalidPathWarning.setVisible(advancedOptions &amp;&amp; userUpdates &amp;&amp; allowUserAppdirPath &amp;&amp; fc_userAppdirPath.isEnabled() &amp;&amp; showInvalidPathWarning);
-
-
-
-// set whether "Set defaults" button should be enabled
-FormComponent fc_setDefaults = formEnvironment.getFormComponentById("SS_SET_DEFAULTS");
-JButton jb_setDefaults = (JButton) fc_setDefaults.getConfigurationObject();
-boolean enableSetDefaults = !( userUpdates &amp;&amp; !allowUserAppdirPath &amp;&amp; userAppdirPath.length() == 0 &amp;&amp; !installerUpdates );
-jb_setDefaults.setEnabled(enableSetDefaults);</property>
+                                  <property name="value" type="string">validateSystemSpaceAdvancedOptionsForm(context, formEnvironment)</property>
                                 </object>
                               </property>
                               <property name="variableName" type="string">allowSetUserAppdirPath</property>
@@ -1307,98 +1243,12 @@ The default value on ${installer:osName} is
 &lt;/html&gt;</property>
                               <property name="inputVerifier">
                                 <object class="com.install4j.api.beans.ScriptProperty">
-                                  <property name="value" type="string">FormComponent fc_advancedOptions = formEnvironment.getFormComponentById("SS_ADVANCED_OPTIONS");
-FormComponent fc_notUsed = formEnvironment.getFormComponentById("SS_NOT_USED");
-FormComponent fc_userUpdates = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_UPDATES");
-FormComponent fc_installerUpdates = formEnvironment.getFormComponentById("SS_ALLOW_INSTALLER_APPDIR_UPDATES");
-FormComponent fc_allowUserAppdirPath = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_PATH");
-FormComponent fc_userAppdirPath = formEnvironment.getFormComponentById("SS_USER_APPDIR_PATH");
-LayoutGroup lg_advancedGroup = formEnvironment.getLayoutGroupById("SS_ADVANCED_OPTIONS_GROUP");
-LayoutGroup lg_setUserAppdirPath = formEnvironment.getLayoutGroupById("SS_SET_USER_APPDIR_PATH");
-
-
-
-// get boolean status of "Enable advanced options" checkbox
-JCheckBox jcb_advancedOptions = (JCheckBox) fc_advancedOptions.getConfigurationObject();
-boolean advancedOptions = jcb_advancedOptions.isSelected();
-
-// set visibility of Advanced options layout group
-lg_advancedGroup.setVisible(advancedOptions);
-fc_notUsed.setVisible(!advancedOptions);
-
-if (!advancedOptions) {
-  return true;
-}
-
-
-
-// get boolean status of "Allow user-space updates" checkbox
-JCheckBox jcb_user = (JCheckBox) fc_userUpdates.getConfigurationObject();
-boolean userUpdates = jcb_user.isSelected();
-
-// set enabled of customised user appdir path group
-lg_setUserAppdirPath.setEnabled(advancedOptions &amp;&amp; userUpdates);
-
-// set enabled of allow installation updates checkbox
-fc_installerUpdates.setEnabled(advancedOptions &amp;&amp; !userUpdates);
-
-
-
-// get boolean status of "Customise the user-space path" checkbox
-JCheckBox jcb_allowUserAppdirPath = (JCheckBox) fc_allowUserAppdirPath.getConfigurationObject();
-boolean allowUserAppdirPath = jcb_allowUserAppdirPath.isSelected();
-
-// set enabled of userAppdirPath text field
-fc_userAppdirPath.setEnabled(advancedOptions &amp;&amp; allowUserAppdirPath &amp;&amp; fc_allowUserAppdirPath.isEnabled());
-
-// get String value of userAppdirPath text field
-JTextField jtf_userAppdirPath = (JTextField) fc_userAppdirPath.getConfigurationObject();
-String userAppdirPath = jtf_userAppdirPath.getText();
-
-
-
-// get boolean status of "Allow installation updates" checkbox
-JCheckBox jcb_installer = (JCheckBox) fc_installerUpdates.getConfigurationObject();
-boolean installerUpdates = jcb_installer.isSelected();
-
-// should we show the No updates warning?
-boolean showNoUpdatesWarning = advancedOptions &amp;&amp; !(userUpdates || installerUpdates);
-FormComponent fc_noUpdatesWarning = formEnvironment.getFormComponentById("SS_NO_UPDATES_WARNING");
-fc_noUpdatesWarning.setVisible(advancedOptions &amp;&amp; showNoUpdatesWarning);
-
-
-
-// should we show the invalid user-space path warning?
-boolean u = userAppdirPath.contains("%u");
-boolean h = userAppdirPath.contains("%h");
-boolean t = userAppdirPath.startsWith("~" + (String)context.getVariable("sys.fileSeparator"));
-
-boolean showInvalidPathWarning = !( userAppdirPath.length() == 0 || u || h || t );
-FormComponent fc_invalidPathWarning = formEnvironment.getFormComponentById("SS_INVALID_USER_APPDIR_PATH_WARNING");
-fc_invalidPathWarning.setVisible(advancedOptions &amp;&amp; userUpdates &amp;&amp; allowUserAppdirPath &amp;&amp; fc_userAppdirPath.isEnabled() &amp;&amp; showInvalidPathWarning);
-
-
-
-// set whether "Set defaults" button should be enabled
-FormComponent fc_setDefaults = formEnvironment.getFormComponentById("SS_SET_DEFAULTS");
-JButton jb_setDefaults = (JButton) fc_setDefaults.getConfigurationObject();
-boolean enableSetDefaults = !( userUpdates &amp;&amp; !allowUserAppdirPath &amp;&amp; userAppdirPath.length() == 0 &amp;&amp; !installerUpdates );
-jb_setDefaults.setEnabled(enableSetDefaults);
-
-
-
-return true;</property>
+                                  <property name="value" type="string">validateSystemSpaceAdvancedOptionsForm(context, formEnvironment, true)</property>
                                 </object>
                               </property>
                               <property name="keyListener">
                                 <object class="com.install4j.api.beans.ScriptProperty">
-                                  <property name="value" type="string">boolean u = text.contains("%u");
-boolean h = text.contains("%h");
-boolean t = text.startsWith("~" + (String)context.getVariable("sys.fileSeparator"));
-
-boolean showInvalidPathWarning = !( text.length() == 0 || u || h || t );
-FormComponent fc_invalidPathWarning = formEnvironment.getFormComponentById("SS_INVALID_USER_APPDIR_PATH_WARNING");
-fc_invalidPathWarning.setVisible(showInvalidPathWarning);
+                                  <property name="value" type="string">showInvalidUserAppdirPathWarning(context, formEnvironment)
 </property>
                                 </object>
                               </property>
@@ -1442,83 +1292,7 @@ Installation updates will be installed into
 &lt;/html&gt;</property>
                           <property name="selectionScript">
                             <object class="com.install4j.api.beans.ScriptProperty">
-                              <property name="value" type="string">FormComponent fc_advancedOptions = formEnvironment.getFormComponentById("SS_ADVANCED_OPTIONS");
-FormComponent fc_notUsed = formEnvironment.getFormComponentById("SS_NOT_USED");
-FormComponent fc_userUpdates = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_UPDATES");
-FormComponent fc_installerUpdates = formEnvironment.getFormComponentById("SS_ALLOW_INSTALLER_APPDIR_UPDATES");
-FormComponent fc_allowUserAppdirPath = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_PATH");
-FormComponent fc_userAppdirPath = formEnvironment.getFormComponentById("SS_USER_APPDIR_PATH");
-LayoutGroup lg_advancedGroup = formEnvironment.getLayoutGroupById("SS_ADVANCED_OPTIONS_GROUP");
-LayoutGroup lg_setUserAppdirPath = formEnvironment.getLayoutGroupById("SS_SET_USER_APPDIR_PATH");
-
-
-
-// get boolean status of "Enable advanced options" checkbox
-JCheckBox jcb_advancedOptions = (JCheckBox) fc_advancedOptions.getConfigurationObject();
-boolean advancedOptions = jcb_advancedOptions.isSelected();
-
-// set visibility of Advanced options layout group
-lg_advancedGroup.setVisible(advancedOptions);
-fc_notUsed.setVisible(!advancedOptions);
-
-if (!advancedOptions) {
-  return;
-}
-
-
-
-// get boolean status of "Allow user-space updates" checkbox
-JCheckBox jcb_user = (JCheckBox) fc_userUpdates.getConfigurationObject();
-boolean userUpdates = jcb_user.isSelected();
-
-// set enabled of customised user appdir path group
-lg_setUserAppdirPath.setEnabled(advancedOptions &amp;&amp; userUpdates);
-
-// set enabled of allow installation updates checkbox
-fc_installerUpdates.setEnabled(advancedOptions &amp;&amp; !userUpdates);
-
-
-
-// get boolean status of "Customise the user-space path" checkbox
-JCheckBox jcb_allowUserAppdirPath = (JCheckBox) fc_allowUserAppdirPath.getConfigurationObject();
-boolean allowUserAppdirPath = jcb_allowUserAppdirPath.isSelected();
-
-// set enabled of userAppdirPath text field
-fc_userAppdirPath.setEnabled(advancedOptions &amp;&amp; allowUserAppdirPath &amp;&amp; fc_allowUserAppdirPath.isEnabled());
-
-// get String value of userAppdirPath text field
-JTextField jtf_userAppdirPath = (JTextField) fc_userAppdirPath.getConfigurationObject();
-String userAppdirPath = jtf_userAppdirPath.getText();
-
-
-
-// get boolean status of "Allow installation updates" checkbox
-JCheckBox jcb_installer = (JCheckBox) fc_installerUpdates.getConfigurationObject();
-boolean installerUpdates = jcb_installer.isSelected();
-
-// should we show the No updates warning?
-boolean showNoUpdatesWarning = advancedOptions &amp;&amp; !(userUpdates || installerUpdates);
-FormComponent fc_noUpdatesWarning = formEnvironment.getFormComponentById("SS_NO_UPDATES_WARNING");
-fc_noUpdatesWarning.setVisible(advancedOptions &amp;&amp; showNoUpdatesWarning);
-
-
-
-// should we show the invalid user-space path warning?
-boolean u = userAppdirPath.contains("%u");
-boolean h = userAppdirPath.contains("%h");
-boolean t = userAppdirPath.startsWith("~" + (String)context.getVariable("sys.fileSeparator"));
-
-boolean showInvalidPathWarning = !( userAppdirPath.length() == 0 || u || h || t );
-FormComponent fc_invalidPathWarning = formEnvironment.getFormComponentById("SS_INVALID_USER_APPDIR_PATH_WARNING");
-fc_invalidPathWarning.setVisible(advancedOptions &amp;&amp; userUpdates &amp;&amp; allowUserAppdirPath &amp;&amp; fc_userAppdirPath.isEnabled() &amp;&amp; showInvalidPathWarning);
-
-
-
-// set whether "Set defaults" button should be enabled
-FormComponent fc_setDefaults = formEnvironment.getFormComponentById("SS_SET_DEFAULTS");
-JButton jb_setDefaults = (JButton) fc_setDefaults.getConfigurationObject();
-boolean enableSetDefaults = !( userUpdates &amp;&amp; !allowUserAppdirPath &amp;&amp; userAppdirPath.length() == 0 &amp;&amp; !installerUpdates );
-jb_setDefaults.setEnabled(enableSetDefaults);</property>
+                              <property name="value" type="string">validateSystemSpaceAdvancedOptionsForm(context, formEnvironment)</property>
                             </object>
                           </property>
                           <property name="variableName" type="string">allowInstallerAppdirUpdates</property>
@@ -1529,94 +1303,20 @@ jb_setDefaults.setEnabled(enableSetDefaults);</property>
                         <serializedBean>
                           <property name="actionScript">
                             <object class="com.install4j.api.beans.ScriptProperty">
-                              <property name="value" type="string">FormComponent fc_advancedOptions = formEnvironment.getFormComponentById("SS_ADVANCED_OPTIONS");
-FormComponent fc_notUsed = formEnvironment.getFormComponentById("SS_NOT_USED");
-FormComponent fc_userUpdates = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_UPDATES");
+                              <property name="value" type="string">FormComponent fc_userUpdates = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_UPDATES");
 FormComponent fc_installerUpdates = formEnvironment.getFormComponentById("SS_ALLOW_INSTALLER_APPDIR_UPDATES");
 FormComponent fc_allowUserAppdirPath = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_PATH");
 FormComponent fc_userAppdirPath = formEnvironment.getFormComponentById("SS_USER_APPDIR_PATH");
-LayoutGroup lg_advancedGroup = formEnvironment.getLayoutGroupById("SS_ADVANCED_OPTIONS_GROUP");
-LayoutGroup lg_setUserAppdirPath = formEnvironment.getLayoutGroupById("SS_SET_USER_APPDIR_PATH");
-
-
 
 // set defaults
-JCheckBox jcb_user = (JCheckBox) fc_userUpdates.getConfigurationObject();
-jcb_user.setSelected(true);
-
-JCheckBox jcb_allowUserAppdirPath = (JCheckBox) fc_allowUserAppdirPath.getConfigurationObject();
-jcb_allowUserAppdirPath.setSelected(false);
-
-JTextField jtf_userAppdirPath = (JTextField) fc_userAppdirPath.getConfigurationObject();
-jtf_userAppdirPath.setText("");
+((JCheckBox) fc_userUpdates.getConfigurationObject()).setSelected(true);
+((JCheckBox) fc_allowUserAppdirPath.getConfigurationObject()).setSelected(false);
+((JTextField) fc_userAppdirPath.getConfigurationObject()).setText("");
+((JCheckBox) fc_installerUpdates.getConfigurationObject()).setSelected(false);
 
-JCheckBox jcb_installer = (JCheckBox) fc_installerUpdates.getConfigurationObject();
-jcb_installer.setSelected(false);
-
-
-
-// get boolean status of "Enable advanced options" checkbox
-JCheckBox jcb_advancedOptions = (JCheckBox) fc_advancedOptions.getConfigurationObject();
-boolean advancedOptions = jcb_advancedOptions.isSelected();
-
-// set visibility of Advanced options layout group
-lg_advancedGroup.setVisible(advancedOptions);
-fc_notUsed.setVisible(!advancedOptions);
-
-if (!advancedOptions) {
-  return;
-}
-
-
-
-// get boolean status of "Allow user-space updates" checkbox
-boolean userUpdates = jcb_user.isSelected();
-
-// set enabled of customised user appdir path group
-lg_setUserAppdirPath.setEnabled(advancedOptions &amp;&amp; userUpdates);
-
-// set enabled of allow installation updates checkbox
-fc_installerUpdates.setEnabled(advancedOptions &amp;&amp; !userUpdates);
-
-
-
-// get boolean status of "Customise the user-space path" checkbox
-boolean allowUserAppdirPath = jcb_allowUserAppdirPath.isSelected();
-
-// set enabled of userAppdirPath text field
-fc_userAppdirPath.setEnabled(advancedOptions &amp;&amp; allowUserAppdirPath &amp;&amp; fc_allowUserAppdirPath.isEnabled());
-
-// get String value of userAppdirPath text field
-String userAppdirPath = jtf_userAppdirPath.getText();
-
-
-
-// get boolean status of "Allow installation updates" checkbox
-boolean installerUpdates = jcb_installer.isSelected();
-
-// should we show the No updates warning?
-boolean showNoUpdatesWarning = advancedOptions &amp;&amp; !(userUpdates || installerUpdates);
-FormComponent fc_noUpdatesWarning = formEnvironment.getFormComponentById("SS_NO_UPDATES_WARNING");
-fc_noUpdatesWarning.setVisible(advancedOptions &amp;&amp; showNoUpdatesWarning);
-
-
-
-// should we show the invalid user-space path warning?
-boolean u = userAppdirPath.contains("%u");
-boolean h = userAppdirPath.contains("%h");
-boolean t = userAppdirPath.startsWith("~" + (String)context.getVariable("sys.fileSeparator"));
-
-boolean showInvalidPathWarning = !( userAppdirPath.length() == 0 || u || h || t );
-FormComponent fc_invalidPathWarning = formEnvironment.getFormComponentById("SS_INVALID_USER_APPDIR_PATH_WARNING");
-fc_invalidPathWarning.setVisible(advancedOptions &amp;&amp; userUpdates &amp;&amp; allowUserAppdirPath &amp;&amp; fc_userAppdirPath.isEnabled() &amp;&amp; showInvalidPathWarning);
-
-
-
-// set whether "Set defaults" button should be enabled
-FormComponent fc_setDefaults = formEnvironment.getFormComponentById("SS_SET_DEFAULTS");
-JButton jb_setDefaults = (JButton) fc_setDefaults.getConfigurationObject();
-boolean enableSetDefaults = !( userUpdates &amp;&amp; !allowUserAppdirPath &amp;&amp; userAppdirPath.length() == 0 &amp;&amp; !installerUpdates );
-jb_setDefaults.setEnabled(enableSetDefaults);</property>
+// revalidate
+validateSystemSpaceAdvancedOptionsForm(context, formEnvironment);
+</property>
                             </object>
                           </property>
                           <property name="buttonText" type="string">Reset advanced options to defaults</property>
@@ -1971,14 +1671,29 @@ context.getBooleanVariable("addToDockAction")</condition>
                     </serializedBean>
                     <condition>context.getBooleanVariable("appendToPathAction")</condition>
                   </action>
+                  <action name="SYSTEMSPACE: Set installationAppdirHash" id="3041" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction" rollbackBarrierExitCode="0">
+                    <serializedBean>
+                      <property name="script">
+                        <object class="com.install4j.api.beans.ScriptProperty">
+                          <property name="value" type="string">String dir = (String)context.getVariable("sys.installationDir");
+System.out.println("##### sys.installationDir = " + dir);
+String hash = getCanonicalFullPathToDirectoryHash(dir);
+System.out.println("##### sys.installationDir hash = " + dir);
+return (Object) hash;
+</property>
+                        </object>
+                      </property>
+                      <property name="variableName" type="string">installationAppdirHash</property>
+                    </serializedBean>
+                  </action>
                   <action name="SYSTEMSPACE: macOS /etc/paths.d entry" id="2953" beanClass="com.install4j.runtime.beans.actions.text.WriteTextFileAction" actionElevationType="elevated" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="file">
                         <object class="java.io.File">
-                          <string>/etc/paths.d/${compiler:APPLICATION_FOLDER}</string>
+                          <string>/etc/paths.d/${compiler:APPLICATION_FOLDER}-${installer:installationAppdirHash}</string>
                         </object>
                       </property>
-                      <property name="text" type="string">/Applications/${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/MacOS</property>
+                      <property name="text" type="string">${installer:sys.installationDir}/${installer:sys.contentDir}/${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/MacOS</property>
                     </serializedBean>
                     <condition>Util.isMacOS()</condition>
                   </action>
@@ -2005,7 +1720,7 @@ context.getBooleanVariable("addToDockAction")</condition>
                   </action>
                 </beans>
               </group>
-              <group name="BOTHSPACE: VMOPTIONS Getdown update properties" id="3000" beanClass="com.install4j.runtime.beans.groups.ActionGroup">
+              <group name="USER/SYSTEMSPACE: VMOPTIONS Getdown update properties" id="3000" beanClass="com.install4j.runtime.beans.groups.ActionGroup">
                 <beans>
                   <action name="Set disableUserDefaultAppdirUpdates" id="3011" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction" rollbackBarrierExitCode="0">
                     <serializedBean>
@@ -3014,13 +2729,7 @@ ${compiler:JALVIEW_APPLICATION_NAME} will now launch.</property>
       <jreBundle jreBundleSource="none" includedJre="${compiler:LINUX_X64_JAVA_VM_TGZ}" manualJreEntry="true" />
     </unixInstaller>
   </mediaSets>
-  <buildIds>
-    <mediaSet refId="743" />
-    <mediaSet refId="878" />
-    <mediaSet refId="2796" />
-    <mediaSet refId="1595" />
-    <mediaSet refId="2782" />
-    <mediaSet refId="1596" />
-    <mediaSet refId="2639" />
+  <buildIds buildAll="false">
+    <mediaSet refId="3046" />
   </buildIds>
 </install4j>