Resolved merge conflicts from develop into JAL-4386
authorRenia Correya <rcorreya001@dundee.ac.uk>
Tue, 3 Sep 2024 10:37:49 +0000 (16:07 +0530)
committerRenia Correya <rcorreya001@dundee.ac.uk>
Tue, 3 Sep 2024 10:37:49 +0000 (16:07 +0530)
148 files changed:
build.gradle
getdown/lib/FJVL_VERSION
getdown/lib/JVL_VERSION
getdown/lib/getdown-core.jar
getdown/lib/getdown-launcher-local.jar
getdown/lib/getdown-launcher.jar
getdown/src/getdown/ant/pom.xml
getdown/src/getdown/core/pom.xml
getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Application.java
getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/EnvConfig.java
getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/LaunchUtil.java
getdown/src/getdown/core/src/main/java/jalview/bin/GetdownLauncherUpdate.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/jalview/util/ErrorLog.java
getdown/src/getdown/core/src/main/java/jalview/util/LaunchUtils.java
getdown/src/getdown/core/src/test/java/com/threerings/getdown/data/EnvConfigTest.java
getdown/src/getdown/launcher/pom.xml
getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/Getdown.java
getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/GetdownApp.java
getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/ProxyUtil.java
getdown/src/getdown/lib/SOURCE_HEADER [deleted file]
getdown/src/getdown/lib/jRegistryKey.dll [deleted file]
getdown/src/getdown/lib/jregistrykey/jregistrykey/1.0/jregistrykey-1.0.jar [deleted file]
getdown/src/getdown/lib/jregistrykey/jregistrykey/1.0/jregistrykey-1.0.pom [deleted file]
getdown/src/getdown/lib/jregistrykey/jregistrykey/maven-metadata-local.xml [deleted file]
getdown/src/getdown/lib/manifest.mf [deleted file]
getdown/src/getdown/mvn_cmd
getdown/src/getdown/pom.xml
gradle.properties
help/markdown/releases/release-2_11_4_0.md
j11lib/getdown-core.jar
j8lib/getdown-core.jar
src/jalview/analysis/AAFrequency.java
src/jalview/analysis/AlignSeq.java
src/jalview/analysis/AlignmentUtils.java
src/jalview/analysis/ConnectivityException.java
src/jalview/analysis/Finder.java
src/jalview/analysis/PaSiMap.java
src/jalview/analysis/ccAnalysis.java
src/jalview/analysis/scoremodels/DistanceScoreModel.java
src/jalview/analysis/scoremodels/FeatureDistanceModel.java
src/jalview/analysis/scoremodels/ScoreModels.java
src/jalview/analysis/scoremodels/SecondaryStructureDistanceModel.java
src/jalview/analysis/scoremodels/SimilarityParams.java
src/jalview/api/AlignViewControllerI.java
src/jalview/api/AlignViewportI.java
src/jalview/api/analysis/ScoreModelI.java
src/jalview/api/analysis/SimilarityParamsI.java
src/jalview/appletgui/AlignFrame.java
src/jalview/bin/Cache.java
src/jalview/bin/GetdownLauncherUpdate.java [new file with mode: 0644]
src/jalview/bin/Jalview.java
src/jalview/bin/JalviewLite.java
src/jalview/bin/Launcher.java
src/jalview/commands/JustifyLeftOrRightCommand.java
src/jalview/controller/AlignViewController.java
src/jalview/datamodel/ContactMapHolder.java
src/jalview/datamodel/ContactMapHolderI.java
src/jalview/datamodel/GroupSetHolder.java
src/jalview/datamodel/PDBEntry.java
src/jalview/datamodel/Profile.java
src/jalview/datamodel/ProfileI.java
src/jalview/datamodel/SecondaryStructureCount.java
src/jalview/datamodel/SequenceGroup.java
src/jalview/datamodel/annotations/AlphaFoldAnnotationRowBuilder.java
src/jalview/ext/ensembl/EnsemblRestClient.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AlignViewport.java
src/jalview/gui/AppJmol.java
src/jalview/gui/CalculationChooser.java
src/jalview/gui/CutAndPasteTransfer.java
src/jalview/gui/Desktop.java
src/jalview/gui/FontChooser.java
src/jalview/gui/PaSiMapPanel.java
src/jalview/gui/PairwiseAlignPanel.java
src/jalview/gui/PopupMenu.java
src/jalview/gui/ProgressBar.java
src/jalview/gui/RotatableCanvas.java
src/jalview/gui/StructureChooser.java
src/jalview/gui/structurechooser/ThreeDBStructureChooserQuerySource.java
src/jalview/jbgui/GAlignFrame.java
src/jalview/jbgui/GCutAndPasteTransfer.java
src/jalview/jbgui/GDesktop.java
src/jalview/jbgui/GPCAPanel.java
src/jalview/jbgui/GPaSiMapPanel.java
src/jalview/jbgui/GPreferences.java
src/jalview/math/Matrix.java
src/jalview/math/MatrixI.java
src/jalview/math/MiscMath.java
src/jalview/project/Jalview2XML.java
src/jalview/renderer/AnnotationRenderer.java
src/jalview/renderer/ResidueShader.java
src/jalview/schemes/ClustalxColourScheme.java
src/jalview/schemes/ResidueProperties.java
src/jalview/util/ArgParserUtils.java
src/jalview/util/Constants.java
src/jalview/util/ErrorLog.java
src/jalview/util/HttpUtils.java
src/jalview/util/LaunchUtils.java
src/jalview/util/UserAgent.java
src/jalview/viewmodel/AlignmentViewport.java
src/jalview/viewmodel/PaSiMapModel.java
src/jalview/workers/SecondaryStructureConsensusThread.java
src/jalview/ws/datamodel/alphafold/MappableContactMatrix.java
src/jalview/ws/datamodel/alphafold/PAEContactMatrix.java
test/jalview/analysis/AlignSeqTest.java
test/jalview/analysis/AlignmentUtilsTests.java
test/jalview/analysis/scoremodels/ScoreModelsTest.java
test/jalview/analysis/scoremodels/SecondaryStructureDistanceModelTest.java
test/jalview/commands/EditCommandTest.java
test/jalview/ext/ensembl/EnsemblGeneTest.java
test/jalview/gui/AssociatePDBFileTest.java
test/jalview/gui/CalculationChooserTest.java
test/jalview/project/Jalview2xmlTests.java
test/jalview/renderer/ResidueShaderTest.java
test/jalview/schemes/ClustalxColourSchemeTest.java
test/jalview/util/FileUtilsTest.java
utils/channels/default/channel_gradle.properties
utils/channels/default/images/jalview_logo-64.png [new file with mode: 0644]
utils/channels/default/images/jalview_logo-64@2x.png [new file with mode: 0644]
utils/channels/develop-SUFFIX/channel_gradle.properties
utils/channels/develop-SUFFIX/images/jalview_develop_logo-64.png [new file with mode: 0644]
utils/channels/develop-SUFFIX/images/jalview_develop_logo-64@2x.png [new file with mode: 0644]
utils/channels/develop/channel_gradle.properties
utils/channels/develop/images/jalview_develop_logo-64.png [new file with mode: 0644]
utils/channels/develop/images/jalview_develop_logo-64@2x.png [new file with mode: 0644]
utils/channels/jalviewjs/channel_gradle.properties
utils/channels/jalviewjs/images/jalview_logo-64.png [new file with mode: 0644]
utils/channels/jalviewjs/images/jalview_logo-64@2x.png [new file with mode: 0644]
utils/channels/release/channel_gradle.properties
utils/channels/release/images/jalview_logo-64.png [new file with mode: 0644]
utils/channels/release/images/jalview_logo-64@2x.png [new file with mode: 0644]
utils/channels/test-release/channel_gradle.properties
utils/channels/test-release/images/jalview_test-release_logo-64.png [new file with mode: 0644]
utils/channels/test-release/images/jalview_test-release_logo-64@2x.png [new file with mode: 0644]
utils/getdown/bin/jalview.ps1
utils/getdown/bin/jalview.sh
utils/getdown/bin/run_other_script.ps1 [new file with mode: 0755]
utils/getdown/bin/run_powershell.bat [moved from utils/getdown/bin/jalview.bat with 100% similarity]
utils/getdown/bin/update.ps1 [new file with mode: 0755]
utils/getdown/bin/update.sh [new file with mode: 0755]
utils/install4j/default.vmoptions [new file with mode: 0644]
utils/install4j/file_associations_auto-install4j10.xml
utils/install4j/file_associations_template-install4j10.xml
utils/install4j/install4j10_template.install4j
utils/install4j/macos-install-jalview.sh [new file with mode: 0755]
utils/install4j/warning.png [new file with mode: 0644]
utils/osx_signing/sign_dmg.sh [new file with mode: 0755]
utils/osx_signing/staple_dmg.sh [new file with mode: 0755]

index 3625ffc..b9b3ad9 100644 (file)
@@ -42,7 +42,6 @@ buildscript {
   dependencies {
     classpath "com.vladsch.flexmark:flexmark-all:0.62.0"
     classpath "org.jsoup:jsoup:1.14.3"
-    classpath "com.eowise:gradle-imagemagick:0.5.1"
     classpath 'ru.vyarus:gradle-use-python-plugin:4.0.0'
   }
 }
@@ -61,7 +60,6 @@ plugins {
 }
 
 repositories {
-  jcenter()
   mavenCentral()
   mavenLocal()
 }
@@ -2313,7 +2311,7 @@ task getdownWebsiteBuild() {
     }
     getdownWebsiteResourceFilenames += file(channelPropsFile).getName()
 
-    // set some getdownTxt_ properties then go through all properties looking for getdownTxt_...
+    // set some getdownTxt_ properties then go through all properties looking for getdown_txt_...
     def props = project.properties.sort { it.key }
     if (getdownAltJavaMinVersion != null && getdownAltJavaMinVersion.length() > 0) {
       props.put("getdown_txt_java_min_version", getdownAltJavaMinVersion)
@@ -2387,8 +2385,15 @@ task getdownWebsiteBuild() {
         into getdownResourceDir
       }
     }
-    
-    def getdownWrapperScripts = [ getdown_bash_wrapper_script, getdown_powershell_wrapper_script, getdown_batch_wrapper_script ]
+
+    def getdownWrapperScripts = [
+      getdown_bash_wrapper_script,
+      getdown_powershell_wrapper_script,
+      getdown_bash_update_script,
+      getdown_powershell_update_script,
+    ]
+    def run_powershell = file( "${jalviewDir}/utils/getdown/${getdown_wrapper_script_dir}/${getdown_run_powershell}" )
+    def run_other_script = file( "${jalviewDir}/utils/getdown/${getdown_wrapper_script_dir}/${getdown_run_other_script}" )
     getdownWrapperScripts.each{ script ->
       def s = file( "${jalviewDir}/utils/getdown/${getdown_wrapper_script_dir}/${script}" )
       if (s.exists()) {
@@ -2398,6 +2403,31 @@ task getdownWebsiteBuild() {
         }
         getdownTextLines += "xresource = ${getdown_wrapper_script_dir}/${script}"
       }
+      def ext = script.toLowerCase(Locale.ROOT).substring(script.lastIndexOf(".") + 1)
+      if ("ps1".equals(ext)) {
+        def base = script.take(script.lastIndexOf("."))
+        def newbase = "update".equals(base) ? "${install4jUnixApplicationFolder}_update" : install4jUnixApplicationFolder
+        if (!newbase.equals(base)) {
+          copy {
+            from run_other_script
+            rename(run_other_script.getName(), "${newbase}.${ext}")
+            into "${getdownAppBaseDir}/${getdown_wrapper_script_dir}"
+            getdownTextLines += "xresource = ${getdown_wrapper_script_dir}/${newbase}.${ext}"
+            filter(ReplaceTokens,
+              beginToken: '__',
+              endToken: '__',
+              tokens: [
+                'OTHERSCRIPT': script
+              ]
+            )          }
+        }
+        copy {
+          from run_powershell
+          rename(run_powershell.getName(), "${newbase}.bat")
+          into "${getdownAppBaseDir}/${getdown_wrapper_script_dir}"
+          getdownTextLines += "xresource = ${getdown_wrapper_script_dir}/${newbase}.bat"
+        }
+      }
     }
 
     def codeFiles = []
@@ -2924,9 +2954,52 @@ task install4jCustomiseDS_Store {
   dependsOn install4jCustomiseDS_StoreAarch64
 }
 
+task install4jDMGVmoptionsFile(type: Copy) {
+  def inputDir = "${jalviewDir}/${install4j_utils_dir}"
+  def outputDir = "${jalviewDir}/${install4j_build_dir}/tmp"
+
+  def installDateTime = getDate("yyyy-MM-dd HH:mm:ss") + " (build time)"
+
+  from(inputDir) {
+    include(string("${install4j_default_vmoptions}"))
+    rename(string("${install4j_default_vmoptions}"), string("${install4j_default_vmoptions}.X64"))
+
+    filter(ReplaceTokens,
+      beginToken: '__',
+      endToken: '__',
+      tokens: [
+        'INSTALLERFILENAME': string("${install4jmacOSArchiveX64DMGFilename}.dmg"),
+        'INSTALLDATETIME': installDateTime
+      ]
+    )
+
+  }
+
+  from(inputDir) {
+    include(string("${install4j_default_vmoptions}"))
+    rename(string("${install4j_default_vmoptions}"), string("${install4j_default_vmoptions}.AARCH64"))
+
+    filter(ReplaceTokens,
+      beginToken: '__',
+      endToken: '__',
+      tokens: [
+        'INSTALLERFILENAME': string("${install4jmacOSArchiveAarch64DMGFilename}.dmg"),
+        'INSTALLDATETIME': installDateTime
+      ]
+    )
+  }
+
+  into outputDir
+
+  inputs.file("${inputDir}/${install4j_default_vmoptions}")
+  outputs.file("${outputDir}/${install4j_default_vmoptions}.X64")
+  outputs.file("${outputDir}/${install4j_default_vmoptions}.AARCH64")
+}
+
 task install4jDMGProcesses {
   dependsOn install4jDMGBackgroundImageProcess
   dependsOn install4jCustomiseDS_Store
+  dependsOn install4jDMGVmoptionsFile
 }
 
 task installerFiles(type: com.install4j.gradle.Install4jTask) {
@@ -2981,7 +3054,8 @@ task installerFiles(type: com.install4j.gradle.Install4jTask) {
     'WRAPPER_LINK': getdownWrapperLink,
     'BASH_WRAPPER_SCRIPT': getdown_bash_wrapper_script,
     'POWERSHELL_WRAPPER_SCRIPT': getdown_powershell_wrapper_script,
-    'BATCH_WRAPPER_SCRIPT': getdown_batch_wrapper_script,
+    'BASH_UPDATE_SCRIPT': getdown_bash_update_script,
+    'POWERSHELL_UPDATE_SCRIPT': getdown_powershell_update_script,
     'WRAPPER_SCRIPT_BIN_DIR': getdown_wrapper_script_dir,
     'MACOSARCHIVE_X64_NAME': install4jmacOSArchiveX64Name,
     'MACOSARCHIVE_AARCH64_NAME': install4jmacOSArchiveAarch64Name,
@@ -2993,7 +3067,7 @@ task installerFiles(type: com.install4j.gradle.Install4jTask) {
     'GETDOWN_ALT_DIR': getdown_app_dir_alt,
     'GETDOWN_INSTALL_DIR': getdown_install_dir,
     'INFO_PLIST_FILE_ASSOCIATIONS_FILE': install4j_info_plist_file_associations,
-    'BUILD_DIR': install4jBuildDir,
+    'BUILD_DIR': install4j_build_dir,
     'APPLICATION_CATEGORIES': install4j_application_categories,
     'APPLICATION_FOLDER': install4jApplicationFolder,
     'UNIX_APPLICATION_FOLDER': install4jUnixApplicationFolder,
@@ -3010,6 +3084,7 @@ task installerFiles(type: com.install4j.gradle.Install4jTask) {
     'INSTALLER_ICON': "${getdownImagesDir}/${install4j_installer_icon}",
     'INSTALLER_MAC_ICON': "${getdownImagesDir}/${install4j_installer_mac_icon}",
     'INSTALLER_WINDOWS_ICON': "${getdownImagesDir}/${install4j_installer_windows_icon}",
+    'TITLE_ICON': "${getdownImagesDir}/${install4j_title_icon}",
     'LOG_FILE': "${install4jUnixApplicationFolder}.log",
   ]
 
index 9a27132..3e5faf8 100644 (file)
@@ -1 +1 @@
-1.8.3-1.5.0_FJVL
+1.8.3-1.5.3_FJVL
index 5446e8b..32c1fa5 100644 (file)
@@ -1 +1 @@
-1.8.3-1.5.0_JVL
+1.8.3-1.5.3_JVL
index 124cf70..652c6c4 100644 (file)
Binary files a/getdown/lib/getdown-core.jar and b/getdown/lib/getdown-core.jar differ
index 45ae248..a393030 100644 (file)
Binary files a/getdown/lib/getdown-launcher-local.jar and b/getdown/lib/getdown-launcher-local.jar differ
index 0057e24..faaba1e 100644 (file)
Binary files a/getdown/lib/getdown-launcher.jar and b/getdown/lib/getdown-launcher.jar differ
index 95aea5a..ff88d94 100644 (file)
@@ -4,7 +4,7 @@
   <parent>
     <groupId>com.threerings.getdown</groupId>
     <artifactId>getdown</artifactId>
-    <version>1.8.3-1.5.0_FJVL</version>
+    <version>1.8.3-1.5.3_FJVL</version>
   </parent>
 
   <artifactId>getdown-ant</artifactId>
index 6cd52f7..8a80df5 100644 (file)
@@ -4,7 +4,7 @@
   <parent>
     <groupId>com.threerings.getdown</groupId>
     <artifactId>getdown</artifactId>
-    <version>1.8.3-1.5.0_FJVL</version>
+    <version>1.8.3-1.5.3_FJVL</version>
   </parent>
 
   <artifactId>getdown-core</artifactId>
index 12c2b64..19f4030 100644 (file)
@@ -23,8 +23,8 @@ import java.nio.file.Files;
 import java.nio.file.StandardCopyOption;
 import java.security.*;
 import java.security.cert.Certificate;
-import java.util.*;
 import java.util.concurrent.*;
+import java.util.*;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.zip.GZIPInputStream;
@@ -1092,7 +1092,7 @@ public class Application
         args.add("-Dlauncher.version=" + Build.version());
         addSystemPropertyIfNotNull(args, "installer.appdir");
         // add the marker indicating the app is running in getdown
-        args.add("-D" + Properties.GETDOWN + "=true");
+        args.add("-D" + com.threerings.getdown.data.Properties.GETDOWN + "=true");
         args.add("-Dsys.install4jVersion=" + Application.i4jVersion);
         addSystemPropertyIfNotNull(args, "installer.template_version");
         addSystemPropertyIfNotNull(args, "installer.logfile");
@@ -2139,6 +2139,27 @@ public class Application
         }
       }
       
+      final String installerPropsFilename = "installer.properties";
+      log.info("Creating " + installerPropsFilename);
+      java.util.Properties installerProps = new java.util.Properties();
+      installerProps.setProperty("installer.appdir", applicationAppDir.getAbsolutePath());
+      installerProps.setProperty("installer.appdirhash", EnvConfig.getAppDirHash());
+      for ( String key: new String[] {"installer.template_version", "installer.application_folder", "installer.icon", "installer.mac_icons", "installer.logfile", "installer.logfile_append"}) {
+          String val = System.getProperty(key);
+          if (val != null) {
+            installerProps.put(key, val);
+          }
+      }
+      for (String prop: installerProps.stringPropertyNames()) {
+        log.info("Adding property '" + prop + "'='" + installerProps.getProperty(prop) + "' to " + installerPropsFilename);
+      }
+      File installerPropsFile = new File(userAppDir, installerPropsFilename);
+      try {
+        installerProps.store(new FileOutputStream(installerPropsFile.getAbsolutePath()), "Created by installer launcher in " + applicationAppDir);
+      } catch (IOException e) {
+        log.warning("Could not write installer properties file '" + installerPropsFile.getAbsolutePath() + "'");
+      }
+      
       MessageDigest md = Digest.getMessageDigest(Digest.VERSION);
       for (Resource rsc: copyResources) {
         String digestHash = digest2.getDigest(rsc);
index e5051de..dd163d3 100644 (file)
@@ -5,6 +5,8 @@
 
 package com.threerings.getdown.data;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
@@ -75,10 +77,7 @@ public final class EnvConfig {
         String appIdProv = null;
         String appBase = null;
         String appBaseProv = null;
-        applicationFolder = System.getProperty("installer.application_folder");
-        installerAppdir = System.getProperty(APPLICATION_APPDIR_PROPERTY);
-        
-        appName = System.getProperty("channel.app_name");
+        setVarsFromProperties();
         
         // start with bootstrap.properties config, if avaialble
         try {
@@ -177,10 +176,11 @@ public final class EnvConfig {
         }
  
         // forced by system property to not use user default appdir
-        if (SysProps.noUpdate() && Boolean.valueOf(System.getProperty("no" + USER_DEFAULT_APPDIR_PROPERTY))) {
+        if (appDir == null && Boolean.valueOf(System.getProperty("no" + USER_DEFAULT_APPDIR_PROPERTY))) {
           appDir = installerAppdir;
-          appDirProv = "no user default";
+          appDirProv = "installer appdir";
           userAppDir = false;
+          notes.add(Note.info("Not using " + USER_DEFAULT_APPDIR_PROPERTY + ", resorting to " + appDirProv + " '" + appDir +"'"));
         } else if (userAppDir && installerAppdir != null) {
           // we're going to try and use a different appdir, check if newer getdown-launcher.jar found there, and restart using that one
           final String getdown = "getdown-launcher.jar";
@@ -255,6 +255,9 @@ public final class EnvConfig {
 
         // ensure that we were able to find an app dir
         if (appDir == null) {
+            if (!SysProps.noUpdate()) {
+              notes.add(Note.error("No appDir could be determined. You may need to set -Dsilent=noupdate -Dno"+USER_DEFAULT_APPDIR_PROPERTY));
+            }
             return null; // caller will report problem to user
         }
 
@@ -344,7 +347,20 @@ public final class EnvConfig {
         this.appArgs = appArgs;
     }
     
-    private static final String getUserAppdir() {
+    /**
+     * compute a hash for the given path string using the getdown digest
+     * @param install_app_dir
+     * @return 8-character hex hash
+     */
+    public static final String getFullPathToDirectoryHash(String install_app_dir)
+    {
+      MessageDigest md = Digest.getMessageDigest(Digest.VERSION);
+      byte[] contents = install_app_dir.getBytes(UTF_8);
+      String hash = StringUtil.hexlate(md.digest(contents));
+      return hash.substring(0,8);
+    }
+    
+    public static final String getUserAppdir() {
       final String noUseDefaultAppDirProperty = "no" + USER_DEFAULT_APPDIR_PROPERTY;
       if (Boolean.valueOf(System.getProperty(noUseDefaultAppDirProperty))) {
         System.err.println("Not using default user appdir because property '" + noUseDefaultAppDirProperty + "' is '" + System.getProperty(noUseDefaultAppDirProperty) + "'");
@@ -356,10 +372,25 @@ public final class EnvConfig {
       }
       String appdirname = applicationFolder == null || applicationFolder.length() == 0 ? ChannelProperties.FALLBACK_APPNAME : applicationFolder;
       String home = System.getProperty("user.home");
+      // for times when using this method without running GetdownApp
+      if (appName == null) {
+        appName = System.getProperty("channel.app_name");
+      }
+      if (installerAppdir == null) {
+        installerAppdir = System.getProperty(APPLICATION_APPDIR_PROPERTY);
+      }
       String appname = StringUtil.isBlank(appName) ? ChannelProperties.FALLBACK_APPNAME : appName;
       final String FS = File.separator;
+      try {
+        setAppDirHash(installerAppdir);
+      } catch (IOException ioex) {
+        System.err.println("Unable to resolve '"+installerAppdir+"' as a proper path on this system.\nNot generating a user local appdir - getdown may fail to update!");
+        return null;
+      }
+      
       String appDataPath;
       String append;
+      boolean addHome = true;
       if (LaunchUtil.isMacOS()) {
         appDataPath = osAppDataPathMap.get("macos");
         append = appname;
@@ -373,8 +404,25 @@ public final class EnvConfig {
         appDataPath = osAppDataPathMap.get("other");
         append = appdirname.toLowerCase(Locale.ROOT);
       }
-      String returnString = home + FS + appDataPath + FS + append + FS + "app";
-      return returnString;
+      String setUserAppDirPath = System.getProperty(SET_USER_APPDIR_PATH);
+      // do not use a setUserAppDirPath with ".." in it
+      if (!StringUtil.isBlank(setUserAppDirPath) && setUserAppDirPath.indexOf("..") == -1) {
+        if (setUserAppDirPath.startsWith("~" + FS)) {
+          setUserAppDirPath = home + setUserAppDirPath.substring(1);
+        }
+        appDataPath = setUserAppDirPath.replaceAll("%u", System.getProperty("user.name")).replaceAll("%h", home);
+        addHome = false;
+      }
+      
+      StringBuilder sb = new StringBuilder();
+      if (addHome) {
+        sb.append(home).append(FS);
+      }
+      sb.append(appDataPath).append(FS);
+      sb.append(append).append(FS);
+      sb.append(getAppDirHash()).append(FS);
+      sb.append("app");
+      return sb.toString();
     }
     
     public static void setRelaunched(boolean b) {
@@ -399,6 +447,20 @@ public final class EnvConfig {
       return appName;
     }
     
+    public static void setVarsFromProperties() {
+      applicationFolder = System.getProperty("installer.application_folder");
+      installerAppdir = System.getProperty(APPLICATION_APPDIR_PROPERTY);
+      appName = System.getProperty("channel.app_name");
+    }
+    
+    public static String getAppDirHash() {
+      return appDirHash;
+    }
+    
+    public static void setAppDirHash(String path) throws IOException {
+      appDirHash = getFullPathToDirectoryHash(new File(path).getCanonicalPath());
+    }
+    
     private static boolean relaunched = false;
     
     private static final String USER_HOME_KEY = "${user.home}";
@@ -409,11 +471,15 @@ public final class EnvConfig {
     
     private static String appName = null;
     
-    private static final String USER_DEFAULT_APPDIR_PROPERTY = "userdefaultappdir";
+    private static String appDirHash = null;
+    
+    public static final String USER_DEFAULT_APPDIR_PROPERTY = "userdefaultappdir";
+    
+    protected static final String APPLICATION_APPDIR_PROPERTY = "installer.appdir";
     
-    private static final String APPLICATION_APPDIR_PROPERTY = "installer.appdir";
+    protected static final String POPULATE_DEFAULT_APPDIR_PROPERTY = "populatedefaultappdir";
     
-    private static final String POPULATE_DEFAULT_APPDIR_PROPERTY= "populatedefaultappdir";
+    protected static final String SET_USER_APPDIR_PATH = "setuserappdirpath";
     
     private static final Map<String,String> osAppDataPathMap;
     
index d6ad03d..3fbff0a 100644 (file)
@@ -13,8 +13,6 @@ import java.nio.file.Files;
 import java.nio.file.Paths;
 import java.util.Locale;
 
-import javax.xml.bind.DatatypeConverter;
-
 import java.security.MessageDigest;
 
 import jalview.util.ChannelProperties;
@@ -149,7 +147,7 @@ public class LaunchUtil
                MessageDigest md = MessageDigest.getInstance(algo);
                md.update(Files.readAllBytes(Paths.get(file.getAbsolutePath())));
                byte[] digest = md.digest();
-               checksum = DatatypeConverter.printHexBinary(digest).toUpperCase(Locale.ROOT);
+               checksum = printHexBinary(digest).toUpperCase(Locale.ROOT);
        } catch (Exception e) {
                System.out.println("Couldn't create "+algo+" digest of "+file.getPath());
        }
@@ -322,4 +320,23 @@ public class LaunchUtil
             // can't grab system properties; we'll just pretend we're not on any of these OSes
         }
     }
+    
+    private static String printHexBinary(byte[] bytes) {
+      if (bytes == null) {
+        return null;
+      }
+      StringBuilder sb = new StringBuilder();
+      for (int i = 0; i < bytes.length; i++) {
+        sb.append(byteToHex(bytes[i]));
+      }
+      return sb.toString();
+    }
+    
+    private static String byteToHex(byte num) {
+        char[] hexDigits = new char[2];
+        hexDigits[0] = Character.forDigit((num >> 4) & 0xF, 16);
+        hexDigits[1] = Character.forDigit((num & 0xF), 16);
+        return new String(hexDigits);
+    }
+
 }
diff --git a/getdown/src/getdown/core/src/main/java/jalview/bin/GetdownLauncherUpdate.java b/getdown/src/getdown/core/src/main/java/jalview/bin/GetdownLauncherUpdate.java
new file mode 100644 (file)
index 0000000..ec1f322
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.bin;
+
+import java.io.File;
+
+import com.threerings.getdown.data.EnvConfig;
+import com.threerings.getdown.util.LaunchUtil;
+
+public class GetdownLauncherUpdate
+{
+  public static void main(String[] args)
+  {
+    EnvConfig.setVarsFromProperties();
+
+    String appdir = args.length > 0 ? args[0] : null;
+    if (appdir == null || appdir.length() == 0)
+    {
+      appdir = System.getProperty("launcher.appdir", null);
+    }
+    if (appdir == null)
+    {
+      appdir = EnvConfig.getUserAppdir();
+    }
+    if (appdir == null)
+    {
+      System.err.println("Not running upgradeGetdown");
+      return;
+    }
+    boolean debug = false;
+    for (int i = 0; i < args.length; i++)
+    {
+      if ("--debug".equals(args[i]))
+      {
+        debug = true;
+        break;
+      }
+    }
+    if (debug)
+    {
+      System.err.println("Running upgradeGetdown");
+    }
+    File appdirFile = new File(appdir);
+    if (!appdirFile.exists())
+    {
+      System.err.println("Directory '" + appdirFile.getAbsolutePath()
+              + "' doesn't exist, cannot update getdown-launcher.jar.");
+      if (Boolean.valueOf(System.getProperty("launcher.update")))
+      {
+        System.exit(1);
+      }
+    }
+    LaunchUtil.upgradeGetdown(new File(appdir, "getdown-launcher-old.jar"),
+            new File(appdir, "getdown-launcher.jar"),
+            new File(appdir, "getdown-launcher-new.jar"));
+  }
+}
index c204783..651073f 100644 (file)
@@ -1,3 +1,23 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
 package jalview.util;
 
 import java.lang.reflect.InvocationTargetException;
@@ -17,11 +37,18 @@ public class ErrorLog
 
   private static String prefix = null;
 
+  private static boolean quiet = false;
+
   public static void setHasConsole(boolean b)
   {
     hasConsole = b;
   }
 
+  public static void setQuiet(boolean b)
+  {
+    quiet = b;
+  }
+
   public static void setPrefix(String s)
   {
     prefix = s;
@@ -45,6 +72,10 @@ public class ErrorLog
   public static void println(String message0, boolean err,
           boolean thisHasConsole)
   {
+    if (!err && quiet)
+    {
+      return;
+    }
     String message = prefix == null ? message0 : prefix + message0;
     if (thisHasConsole && hasConsole)
     {
index 186745d..ccf9992 100644 (file)
@@ -621,7 +621,7 @@ public class LaunchUtils
     }
     String v0 = getJarImplementationVersion(f0);
     String v1 = getJarImplementationVersion(f1);
-    syserr(true, false,
+    syserr(v0 != null && !v0.equals(v1), false,
             "Got launcher versions '" + v0 + "' and '" + v1 + "'");
     return compareGetdownLauncherJarVersions(v0, v1);
   }
index 6178651..02deaa8 100644 (file)
@@ -139,4 +139,42 @@ public class EnvConfigTest {
         checkAppBase(cfg, TESTBASE);
         checkNoAppArgs(cfg);
     }
+    
+    /**
+     * check that the appdir hash distinguishes between different base-app paths
+     */
+    @Test public void testDifferentUserappdirHash() {
+      String envhash1 = EnvConfig.getFullPathToDirectoryHash("/Applications/Getdown.app/");
+      String envhash2 = EnvConfig.getFullPathToDirectoryHash("/Applications/Getdown 1.app/");
+      assertTrue(!envhash1.equals(envhash2));
+    }
+    
+    /**
+     * check whether userdefaultappdir can be overrridden by nouserdefaultappdir
+     */
+    @Test public void checkUserAppDir() {
+      List<EnvConfig.Note> notes = new ArrayList<>();
+      System.getProperties().setProperty(EnvConfig.APPLICATION_APPDIR_PROPERTY, "/Applications/Getdown.app");
+      System.getProperties().setProperty("userdefaultappdir","true");
+      EnvConfig cfg = sysPropsConfig(notes, "appdir", CWD);
+      assertTrue(cfg!=null);
+      hasUserAppDir();
+      
+      System.getProperties().setProperty("nouserdefaultappdir","true");
+      cfg = sysPropsConfig(notes, "appdir", CWD);
+      assertTrue(cfg!=null);
+      noUserAppDir();
+      
+      System.getProperties().remove("nouserdefaultappdir");
+      cfg = sysPropsConfig(notes, "appdir", CWD);
+      assertTrue(cfg!=null);
+      hasUserAppDir();
+    }
+    
+    public void noUserAppDir() {
+      assertTrue(EnvConfig.getUserAppdir()==null);
+    }
+    public void hasUserAppDir() {
+      assertTrue(EnvConfig.getUserAppdir()!=null && !"".equals(EnvConfig.getUserAppdir()));
+    }
 }
index fbd20cf..9c72413 100644 (file)
@@ -4,7 +4,7 @@
   <parent>
     <groupId>com.threerings.getdown</groupId>
     <artifactId>getdown</artifactId>
-    <version>1.8.3-1.5.0_FJVL</version>
+    <version>1.8.3-1.5.3_FJVL</version>
   </parent>
 
   <artifactId>getdown-launcher</artifactId>
index 653bf7a..b0b9a36 100644 (file)
@@ -135,7 +135,7 @@ public abstract class Getdown extends Thread
 
         // determine whether or not we can write to our install directory
         File instdir = _app.getLocalPath("");
-        if (!instdir.canWrite()) {
+        if (!instdir.canWrite() && !SysProps.noUpdate()) {
             String path = instdir.getPath();
             if (path.equals(".")) {
                 path = System.getProperty("user.dir");
index 1283f11..bcb0f75 100644 (file)
@@ -65,7 +65,11 @@ public class GetdownApp
   public static Getdown start (String[] argv) throws Exception {
     jalview.util.ErrorLog.setHasConsole(false);
     jalview.util.ErrorLog.setPrefix("LAUNCHER: ");
-    jalview.util.ErrorLog.outPrintln("start of logging");
+    if (SysProps.silent() && !SysProps.launchInSilent() && "true".equals(System.getProperty("launcher.update", "false"))) {
+      jalview.util.ErrorLog.outPrintln("Running update only");
+    } else {
+      jalview.util.ErrorLog.outPrintln("Start of logging");
+    }
     List<EnvConfig.Note> notes = new ArrayList<>();
     boolean append = false;
     EnvConfig envc = EnvConfig.create(argv, notes, GetdownApp.class);
index cb51ca4..291f854 100644 (file)
@@ -28,6 +28,7 @@ import ca.beq.util.win32.registry.RegistryValue;
 import ca.beq.util.win32.registry.RootKey;
 
 import com.threerings.getdown.data.Application;
+import com.threerings.getdown.data.SysProps;
 import com.threerings.getdown.spi.ProxyAuth;
 import com.threerings.getdown.util.Config;
 import com.threerings.getdown.util.ConnectionUtil;
@@ -165,7 +166,9 @@ public class ProxyUtil {
     public static void configProxy (Application app, String host, String port,
                                     String username, String password) {
         // save our proxy host and port in a local file
-        saveProxy(app, host, port);
+        if (!SysProps.noUpdate()) {
+          saveProxy(app, host, port);
+        }
 
         // save our credentials via the SPI
         if (!StringUtil.isBlank(username) && !StringUtil.isBlank(password)) {
@@ -211,7 +214,7 @@ public class ProxyUtil {
     public static void initProxy (Application app, String host, String port,
                                   String username, String password)
     {
-System.out.println("**** initProxy(app, '"+host+"', "+port+", '"+username+"', "+(password==null?"null":"*x"+password.length())+")");
+        log.info("initProxy:", "app", app, "host", host, "port", port, "username", username, "password", "("+(password==null?"null":"*x"+password.length())+")");
         // check whether we have saved proxy credentials
         String appDir = app.getAppDir().getAbsolutePath();
         ServiceLoader<ProxyAuth> loader = ServiceLoader.load(ProxyAuth.class);
diff --git a/getdown/src/getdown/lib/SOURCE_HEADER b/getdown/src/getdown/lib/SOURCE_HEADER
deleted file mode 100644 (file)
index 43271fe..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-//
-// Getdown - application installer, patcher and launcher
-// Copyright (C) 2004-2018 Getdown authors
-// https://github.com/threerings/getdown/blob/master/LICENSE
-
diff --git a/getdown/src/getdown/lib/jRegistryKey.dll b/getdown/src/getdown/lib/jRegistryKey.dll
deleted file mode 100644 (file)
index 5746728..0000000
Binary files a/getdown/src/getdown/lib/jRegistryKey.dll and /dev/null differ
diff --git a/getdown/src/getdown/lib/jregistrykey/jregistrykey/1.0/jregistrykey-1.0.jar b/getdown/src/getdown/lib/jregistrykey/jregistrykey/1.0/jregistrykey-1.0.jar
deleted file mode 100644 (file)
index 5100795..0000000
Binary files a/getdown/src/getdown/lib/jregistrykey/jregistrykey/1.0/jregistrykey-1.0.jar and /dev/null differ
diff --git a/getdown/src/getdown/lib/jregistrykey/jregistrykey/1.0/jregistrykey-1.0.pom b/getdown/src/getdown/lib/jregistrykey/jregistrykey/1.0/jregistrykey-1.0.pom
deleted file mode 100644 (file)
index 226a7d7..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-  <modelVersion>4.0.0</modelVersion>
-  <groupId>jregistrykey</groupId>
-  <artifactId>jregistrykey</artifactId>
-  <version>1.0</version>
-  <description>POM was created from install:install-file</description>
-</project>
diff --git a/getdown/src/getdown/lib/jregistrykey/jregistrykey/maven-metadata-local.xml b/getdown/src/getdown/lib/jregistrykey/jregistrykey/maven-metadata-local.xml
deleted file mode 100644 (file)
index 1a8a725..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<metadata>
-  <groupId>jregistrykey</groupId>
-  <artifactId>jregistrykey</artifactId>
-  <version>1.0</version>
-  <versioning>
-    <versions>
-      <version>1.0</version>
-    </versions>
-    <lastUpdated>20101118155146</lastUpdated>
-  </versioning>
-</metadata>
diff --git a/getdown/src/getdown/lib/manifest.mf b/getdown/src/getdown/lib/manifest.mf
deleted file mode 100644 (file)
index 3be50cc..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-Main-Class: com.threerings.getdown.launcher.Getdown
-Permissions: all-permissions
-Application-Name: Getdown
-Codebase: *
-Application-Library-Allowable-Codebase: *
-Caller-Allowable-Codebase: *
index 41725cf..cd2a39a 100755 (executable)
@@ -3,7 +3,7 @@
 if [ x$JVLVERSION != x ]; then
   export VERSION=$JVLVERSION
 else
-  export VERSION=1.8.3-1.5.0_JVL
+  export VERSION=1.8.3-1.5.3_JVL
 fi
 
 if [ x${VERSION%_JVL} = x$VERSION ]; then
index 3eb0565..6f2cd69 100644 (file)
@@ -10,7 +10,7 @@
   <groupId>com.threerings.getdown</groupId>
   <artifactId>getdown</artifactId>
   <packaging>pom</packaging>
-  <version>1.8.3-1.5.0_FJVL</version>
+  <version>1.8.3-1.5.3_FJVL</version>
 
   <name>getdown</name>
   <description>An application installer and updater.</description>
index 041dc13..823264c 100644 (file)
@@ -83,7 +83,6 @@ getdown_lib_dir = getdown/lib
 getdown_launcher = getdown-launcher.jar
 getdown_launcher_local = getdown-launcher-local.jar
 getdown_launcher_new = getdown-launcher-new.jar
-getdown_core = getdown/lib/getdown-core.jar
 getdown_build_properties = build_properties
 getdown_launch_jvl_name = channel_launch
 getdown_images_dir = utils/getdown
@@ -98,6 +97,7 @@ getdown_txt_max_concurrent_downloads = 10
 # now got better (dynamic) defaults when jvmmem* not set
 #getdown_txt_jalview.jvmmempc = 90
 #getdown_txt_jalview.jvmmemmax = 32G
+getdown_txt_resource = getdown/lib/getdown-core.jar
 getdown_txt_strict_comments = true
 getdown_txt_ui.progress_sync_before_shown = true
 getdown_txt_ui.progress_sync_after_shown = false
@@ -158,13 +158,17 @@ install4j_dmg_volume_icon = jalview-VolumeIcon.icns
 install4j_installer_icon = jalview_installer.png
 install4j_installer_mac_icon = jalview_installer.icns
 install4j_installer_windows_icon = jalview_installer.ico
+install4j_default_vmoptions = default.vmoptions
 jalview_customise_ds_store = utils/macos_dmg/jalview_customise_dsstore.py
 
 
 getdown_wrapper_script_dir = bin
 getdown_bash_wrapper_script = jalview.sh
 getdown_powershell_wrapper_script = jalview.ps1
-getdown_batch_wrapper_script = jalview.bat
+getdown_bash_update_script = update.sh
+getdown_powershell_update_script = update.ps1
+getdown_run_other_script = run_other_script.ps1
+getdown_run_powershell = run_powershell.bat
 
 OSX_KEYSTORE =
 OSX_KEYPASS =
index 0d4017a..f27b140 100644 (file)
@@ -1,23 +1,51 @@
 ---
 version: 2.11.4.0
-date: 2024-06-30
+date: 2024-09-03
 channel: "develop"
 ---
 
 ## New Features
 
+- <!-- JAL-4159,JAL-4390 --> Calculate PASiMap projection for sequences - ported by Thomas Morell ( U. Konstanz)
 - <!-- JAL-4392 --> Consensus secondary structure visualization for alignments
+- <!-- JAL-4411 --> Show data source for 'reference annotation' from 3D structure (e.g. Secondary Structure)
 - <!-- JAL-4386 --> Calculate tree or PCA using secondary structure annotation
-- <!-- JAL-4159 --> Calculate PASiMap projection for sequences - ported by Thomas Morell ( U. Konstanz)
 - <!-- JAL-789,JAL-4257 --> allow adjustment of gap opening, extension, and score model for built in pairwise alignment
 
+### Experimental Features (enable via Desktop's tools menu)
+
+- <!-- JAL-4420 --> Jalview sensibly handles opening or a drag'n'drop of several .features, .annotations and .newick files onto an alignment
+<!-- not yet! - -- JAL-4366    Linked visualisation of Foldseek 3Di MSAs with corresponding 3D structures -->
 
 ### development and deployment
 
+- <!-- JAL-3830 -->    Add a command-line wrapper script to macOS bundle, linux and Windows installations (bash, powershell and .bat wrappers)
+- <!-- JAL-3384 -->    Redirect stdout and stderr to file when launched from getdown
+- <!-- JAL-3845 -->    Add error message when launched directly from Jalview Installer DMG volume
+- <!-- JAL-3631,JAL-4104 -->   Allow jalview auto-updates to download to and work from separate user-space directory
+- <!-- JAL-3978, JAL-3196 -->  Gradle build with install4j launchers
+- <!-- JAL-4409, JAL-4160 -->  Implement in Jalview (and getdown for JVL) the use of specific scheme names for different channels (e.g. jalviewd://)
+- <!-- JAL-4428 -->    integrate OSX codesigning scripts in jalview's repository
+- <!-- JAL-3063 -->    new documentation describing how jalview project XML schema is maintained (in doc/jalview-projects.md)
+- <!-- JAL-3018,JAL-4449, JAL-4328 --> updated Jalview's Ensembl's client for v15.8 of the REST API - change in BRAF query returned by ensembl Genes for chicken for GRCg7b (now ENSGALG00010013466, was ENSGALG00000012865)
 
 ## Issues Resolved
+- <!-- JAL-1054 -->    jalview does not fetch from a URL containing a redirect
+- <!-- JAL-4414 -->    HTTP errors are often interpreted to the user as a file format error - and FileNotFound errors are not propagated from Web or Local file import
+- <!-- JAL-3409 -->    launcher.log from jalview runtime missing newlines and also lacks stdout/stderr reported in Jalview Console
+- <!-- JAL-4072 -->    Possible "Zip Slip Vulnerability" in getdown
+- <!-- JAL-4417 -->    jalview.bin.CommandsTest.headlessOrGuiImageOutputTest produces different images for --gui and --headless when using certain .jalview_properties
+- <!-- JAL-4421 -->    Release installer DMG does not display the background image
+- <!-- JAL-4358 -->    Getdown splash screen should disappear as soon as the Jalview Desktop is created/visible
 
+### development and deployment
 
-### New Known Issues
+- <!-- JAL-4111 --> removed gradle build's dependency on gradle-imagemagick plugin to allow building on windows (still need imagemagick installed on path for splashscreen commit watermark)
+- <!-- JAL-4397 -->    fixed inconsistencies in test suite on different platforms
+- <!-- JAL-4398 --> The rendering of the text "Window" for the Window menu item is different to the other menus when testing in Linux 
 
 
+### New Known Issues
+- <!-- JAL-4443 -->     PaSiMap not available in JalviewJS
+- <!-- JAL-4100 -->            Jalview ignores ncrna genes when importing from genbank format files
+- <!-- JAL-4450 --> Sequence letter aspect ratio not set correctly when aspect ratio is very narrow after middle-mouse drag
index 124cf70..652c6c4 100644 (file)
Binary files a/j11lib/getdown-core.jar and b/j11lib/getdown-core.jar differ
index 124cf70..652c6c4 100644 (file)
Binary files a/j8lib/getdown-core.jar and b/j8lib/getdown-core.jar differ
index 7278a80..b3a912c 100755 (executable)
@@ -192,13 +192,12 @@ public class AAFrequency
     // jalview.bin.Console.outPrintln(elapsed);
   }
 
-  
   public static final ProfilesI calculateSS(List<SequenceI> list, int start,
           int end, String source)
   {
     return calculateSS(list, start, end, false, source);
   }
-  
+
   public static final ProfilesI calculateSS(List<SequenceI> sequences,
           int start, int end, boolean profile, String source)
   {
@@ -226,7 +225,7 @@ public class AAFrequency
       return reply;
     }
   }
-  
+
   public static final ProfilesI calculateSS(final SequenceI[] sequences,
           int width, int start, int end, boolean saveFullProfile, String source)
   {
@@ -234,14 +233,14 @@ public class AAFrequency
     int seqCount = sequences.length;
     
     int seqWithSSCount = 0;
-    
+
     ProfileI[] result = new ProfileI[width];
 
     for (int column = start; column < end; column++)
     {
-      
+
       int ssCount = 0;
-    
+
       SecondaryStructureCount ssCounts = new SecondaryStructureCount();
 
       for (int row = 0; row < seqCount; row++)
@@ -252,7 +251,7 @@ public class AAFrequency
                   "WARNING: Consensus skipping null sequence - possible race condition.");
           continue;
         }
-        
+
         char c = sequences[row].getCharAt(column);
         List<AlignmentAnnotation> annots = AlignmentUtils.getAlignmentAnnotationForSource(sequences[row], source);
         if(annots!=null) {
@@ -287,7 +286,6 @@ public class AAFrequency
       ProfileI profile = new Profile(maxSS, ssCount, gapCount, 
               maxSSCount, seqWithSSCount);
 
-
       if (saveFullProfile)
       {
         profile.setSSCounts(ssCounts);
@@ -394,29 +392,28 @@ public class AAFrequency
     // long elapsed = System.currentTimeMillis() - now;
     // jalview.bin.Console.outPrintln(-elapsed);
   }
-  
- public static void completeSSConsensus(AlignmentAnnotation ssConsensus,
-         ProfilesI profiles, int startCol, int endCol, boolean ignoreGaps,
-         boolean showSequenceLogo, long nseq)
- {
-   // long now = System.currentTimeMillis();
-   if (ssConsensus == null || ssConsensus.annotations == null
-           || ssConsensus.annotations.length < endCol)
-   {
-     /*
+
+  public static void completeSSConsensus(AlignmentAnnotation ssConsensus,
+          ProfilesI profiles, int startCol, int endCol, boolean ignoreGaps,
+          boolean showSequenceLogo, long nseq)
+  {
+    // long now = System.currentTimeMillis();
+    if (ssConsensus == null || ssConsensus.annotations == null
+            || ssConsensus.annotations.length < endCol)
+    {
+      /*
       * called with a bad alignment annotation row 
       * wait for it to be initialised properly
       */
-     return;
-   }
+      return;
+    }
 
-   for (int i = startCol; i < endCol; i++)
-   {
-     ProfileI profile = profiles.get(i);
-     if (profile == null)
-     {
-       /*
+    for (int i = startCol; i < endCol; i++)
+    {
+      ProfileI profile = profiles.get(i);
+      if (profile == null)
+      {
+        /*
         * happens if sequences calculated over were 
         * shorter than alignment width
         */
@@ -430,10 +427,10 @@ public class AAFrequency
      
      final int dp = getPercentageDp(nseq);
 
-     float value = profile.getSSPercentageIdentity(ignoreGaps);
+      float value = profile.getSSPercentageIdentity(ignoreGaps);
 
-     String description = getSSTooltip(profile, value, showSequenceLogo,
-             ignoreGaps, dp);
+      String description = getSSTooltip(profile, value, showSequenceLogo,
+              ignoreGaps, dp);
 
      String modalSS = profile.getModalSS();
      if ("".equals(modalSS))
@@ -560,7 +557,7 @@ public class AAFrequency
     }
     return description;
   }
-  
+
   static String getSSTooltip(ProfileI profile, float pid,
           boolean showSequenceLogo, boolean ignoreGaps, int dp)
   {
@@ -616,27 +613,28 @@ public class AAFrequency
   {
     char[] symbols;
     int[] values;
-    
+
     if (profile.getCounts() != null)
     {
       ResidueCount counts = profile.getCounts();
       SymbolCounts symbolCounts = counts.getSymbolCounts();
       symbols = symbolCounts.symbols;
       values = symbolCounts.values;
-      
+
     }
-    else if(profile.getSSCounts() != null) 
+    else if (profile.getSSCounts() != null)
     {
       SecondaryStructureCount counts = profile.getSSCounts();
       // to do
-      SecondaryStructureCount.SymbolCounts symbolCounts = counts.getSymbolCounts();
+      SecondaryStructureCount.SymbolCounts symbolCounts = counts
+              .getSymbolCounts();
       symbols = symbolCounts.symbols;
       values = symbolCounts.values;
     }
-    else {
+    else
+    {
       return null;
     }
-    
 
     QuickSort.sort(values, symbols);
     int totalPercentage = 0;
index e806d2b..1a12681 100755 (executable)
@@ -58,10 +58,10 @@ public class AlignSeq
   private static final int DEFAULT_OPENCOST = 120;
 
   private static final int DEFAULT_EXTENDCOST = 20;
-  
-  private int GAP_OPEN_COST=DEFAULT_OPENCOST;
 
-  private int GAP_EXTEND_COST=DEFAULT_EXTENDCOST;
+  private int GAP_OPEN_COST = DEFAULT_OPENCOST;
+
+  private int GAP_EXTEND_COST = DEFAULT_EXTENDCOST;
 
   private static final int GAP_INDEX = -1;
 
@@ -100,11 +100,11 @@ public class AlignSeq
   int[] aseq1;
 
   int[] aseq2;
-  
+
   /*
    * matches in alignment
    */
-  int match=-1;
+  int match = -1;
 
   public String astr1 = "";
 
@@ -129,9 +129,9 @@ public class AlignSeq
 
   public float maxscore;
 
-  public float meanScore;      //needed for PaSiMap
+  public float meanScore; // needed for PaSiMap
 
-  public int hypotheticMaxScore;       // needed for PaSiMap
+  public int hypotheticMaxScore; // needed for PaSiMap
 
   int prev = 0;
 
@@ -156,6 +156,7 @@ public class AlignSeq
     GAP_OPEN_COST = opencost;
     GAP_EXTEND_COST = extcost;
   }
+
   public AlignSeq(SequenceI s1, SequenceI s2, String type)
   {
     seqInit(s1, s1.getSequenceAsString(), s2, s2.getSequenceAsString(),
@@ -182,18 +183,18 @@ public class AlignSeq
   public AlignSeq(SequenceI s1, SequenceI s2, String type, int opencost,
           int extcost)
   {
-    this(s1,s2,type);
-    GAP_OPEN_COST=opencost;
-    GAP_EXTEND_COST=extcost;
+    this(s1, s2, type);
+    GAP_OPEN_COST = opencost;
+    GAP_EXTEND_COST = extcost;
   }
 
   public AlignSeq(SequenceI s12, String string1, SequenceI s22,
           String string2, String type2, int defaultOpencost,
           int defaultExtendcost)
   {
-    this(s12,string1,s22,string2,type2);
-    GAP_OPEN_COST=defaultOpencost;
-    GAP_EXTEND_COST=defaultExtendcost;
+    this(s12, string1, s22, string2, type2);
+    GAP_OPEN_COST = defaultOpencost;
+    GAP_EXTEND_COST = defaultExtendcost;
   }
 
   /**
@@ -207,10 +208,10 @@ public class AlignSeq
   }
 
   /**
-  * returns the overall score of the alignment
-  *
-  * @return
-  */
+   * returns the overall score of the alignment
+   *
+   * @return
+   */
   public float getAlignmentScore()
   {
     return alignmentScore;
@@ -333,27 +334,31 @@ public class AlignSeq
             s2.getDatasetSequence() == null ? s2 : s2.getDatasetSequence());
     return alSeq2;
   }
+
   /**
    * fraction of seq2 matched in the alignment
+   * 
    * @return NaN or [0..1]
    */
   public double getS2Coverage()
   {
-    if (match>=0)
+    if (match >= 0)
     {
-      return ((double)match)/((double)s2.getEnd()-s2.getStart()+1);
+      return ((double) match) / ((double) s2.getEnd() - s2.getStart() + 1);
     }
     return Double.NaN;
   }
+
   /**
    * fraction of seq1 matched in the alignment
+   * 
    * @return NaN or [0..1]
    */
   public double getS1Coverage()
   {
-    if (match>=0)
+    if (match >= 0)
     {
-      return ((double)match)/((double)s1.getEnd()-s1.getStart()+1);
+      return ((double) match) / ((double) s1.getEnd() - s1.getStart() + 1);
     }
     return Double.NaN;
   }
@@ -375,13 +380,14 @@ public class AlignSeq
   public void seqInit(SequenceI s1, String string1, SequenceI s2,
           String string2, String type)
   {
-    seqInit(s1,string1,s2,string2,type,GAP_OPEN_COST,GAP_EXTEND_COST);
+    seqInit(s1, string1, s2, string2, type, GAP_OPEN_COST, GAP_EXTEND_COST);
   }
+
   public void seqInit(SequenceI s1, String string1, SequenceI s2,
-          String string2, String type, int opening,int extension)
+          String string2, String type, int opening, int extension)
   {
-    GAP_OPEN_COST=opening;
-    GAP_EXTEND_COST=extension;
+    GAP_OPEN_COST = opening;
+    GAP_EXTEND_COST = extension;
     this.s1 = s1;
     this.s2 = s2;
     setDefaultParams(type);
@@ -473,13 +479,12 @@ public class AlignSeq
 
     aseq1 = new int[seq1.length + seq2.length];
     aseq2 = new int[seq1.length + seq2.length];
-    match=0;
+    match = 0;
     StringBuilder sb1 = new StringBuilder(aseq1.length);
     StringBuilder sb2 = new StringBuilder(aseq2.length);
 
     count = (seq1.length + seq2.length) - 1;
 
-
     while (i > 0 && j > 0)
     {
       aseq1[count] = seq1[i];
@@ -523,12 +528,12 @@ public class AlignSeq
     {
       aseq2[count] = seq2[j];
       sb2.append(s2str.charAt(j));
-      if (aseq1[count]!=GAP_INDEX) {
+      if (aseq1[count] != GAP_INDEX)
+      {
         match++;
       }
     }
 
-
     /*
      * we built the character strings backwards, so now
      * reverse them to convert to sequence strings
@@ -570,7 +575,7 @@ public class AlignSeq
     int trace;
     maxscore = score[i][j] / 10f;
 
-    //prepare trailing gaps
+    // prepare trailing gaps
     while ((i < seq1.length - 1) || (j < seq2.length - 1))
     {
       i++;
@@ -587,20 +592,22 @@ public class AlignSeq
 
     count = (seq1.length + seq2.length) - 1;
 
-    //get trailing gaps
+    // get trailing gaps
     while ((i >= seq1.length) || (j >= seq2.length))
     {
       if (i >= seq1.length)
       {
-       aseq1[count] = GAP_INDEX;
-       sb1.append("-");
-       aseq2[count] = seq2[j];
-       sb2.append(s2str.charAt(j));
-      } else if (j >= seq2.length) {
-       aseq1[count] = seq1[i];
-       sb1.append(s1str.charAt(i));
-       aseq2[count] = GAP_INDEX;
-       sb2.append("-");
+        aseq1[count] = GAP_INDEX;
+        sb1.append("-");
+        aseq2[count] = seq2[j];
+        sb2.append(s2str.charAt(j));
+      }
+      else if (j >= seq2.length)
+      {
+        aseq1[count] = seq1[i];
+        sb1.append(s1str.charAt(i));
+        aseq2[count] = GAP_INDEX;
+        sb2.append("-");
       }
       i--;
       j--;
@@ -644,18 +651,20 @@ public class AlignSeq
     aseq2[count] = seq2[j];
     sb2.append(s2str.charAt(j));
 
-    //get initial gaps
+    // get initial gaps
     while (j > 0 || i > 0)
     {
       if (j > 0)
       {
-       j--;
-       sb1.append("-");
-       sb2.append(s2str.charAt(j));
-      } else if (i > 0) {
-       i--;
-       sb1.append(s1str.charAt(i));
-       sb2.append("-");
+        j--;
+        sb1.append("-");
+        sb2.append(s2str.charAt(j));
+      }
+      else if (i > 0)
+      {
+        i--;
+        sb1.append(s1str.charAt(i));
+        sb2.append("-");
       }
     }
 
@@ -853,7 +862,7 @@ public class AlignSeq
   {
     int n = seq1.length;
     int m = seq2.length;
-    final int GAP_EX_COST=GAP_EXTEND_COST;
+    final int GAP_EX_COST = GAP_EXTEND_COST;
     final int GAP_OP_COST = GAP_OPEN_COST;
     // top left hand element
     score[0][0] = scoreMatrix.getPairwiseScore(s1str.charAt(0),
@@ -871,8 +880,7 @@ public class AlignSeq
 
       float pairwiseScore = scoreMatrix.getPairwiseScore(s1str.charAt(0),
               s2str.charAt(j));
-      score[0][j] = max(pairwiseScore * 10, -GAP_OP_COST,
-              -GAP_EX_COST);
+      score[0][j] = max(pairwiseScore * 10, -GAP_OP_COST, -GAP_EX_COST);
 
       traceback[0][j] = 1;
     }
@@ -1076,13 +1084,15 @@ public class AlignSeq
   public static AlignSeq doGlobalNWAlignment(SequenceI s1, SequenceI s2,
           String type)
   {
-    return doGlobalNWAlignment(s1, s2, type, DEFAULT_OPENCOST,DEFAULT_EXTENDCOST);
+    return doGlobalNWAlignment(s1, s2, type, DEFAULT_OPENCOST,
+            DEFAULT_EXTENDCOST);
   }
+
   public static AlignSeq doGlobalNWAlignment(SequenceI s1, SequenceI s2,
-          String type, int opencost,int extcost)
+          String type, int opencost, int extcost)
   {
-  
-    AlignSeq as = new AlignSeq(s1, s2, type,opencost,extcost);
+
+    AlignSeq as = new AlignSeq(s1, s2, type, opencost, extcost);
 
     as.calcScoreMatrix();
     as.traceAlignment();
@@ -1359,42 +1369,45 @@ public class AlignSeq
   }
 
   /**
-  * calculate the mean score of the alignment
-  * mean score is equal to the score of an alignmenet of two sequences with randomly shuffled AA sequence composited of the same AA as the two original sequences
-  *
-  */
+   * calculate the mean score of the alignment mean score is equal to the score
+   * of an alignmenet of two sequences with randomly shuffled AA sequence
+   * composited of the same AA as the two original sequences
+   *
+   */
   public void meanScore()
   {
-    int length = indelfreeAstr1.length();      //both have the same length
-    //create HashMap for counting residues in each sequence
+    int length = indelfreeAstr1.length(); // both have the same length
+    // create HashMap for counting residues in each sequence
     HashMap<Character, Integer> seq1ResCount = new HashMap<Character, Integer>();
     HashMap<Character, Integer> seq2ResCount = new HashMap<Character, Integer>();
 
-    // for both sequences (String indelfreeAstr1 or 2) create a key for the residue and add 1 each time its encountered
-    for (char residue: indelfreeAstr1.toCharArray())
+    // for both sequences (String indelfreeAstr1 or 2) create a key for the
+    // residue and add 1 each time its encountered
+    for (char residue : indelfreeAstr1.toCharArray())
     {
       seq1ResCount.putIfAbsent(residue, 0);
       seq1ResCount.replace(residue, seq1ResCount.get(residue) + 1);
     }
-    for (char residue: indelfreeAstr2.toCharArray())
+    for (char residue : indelfreeAstr2.toCharArray())
     {
       seq2ResCount.putIfAbsent(residue, 0);
       seq2ResCount.replace(residue, seq2ResCount.get(residue) + 1);
     }
 
-    // meanscore = for each residue pair get the number of appearance and add (countA * countB * pairwiseScore(AB))
+    // meanscore = for each residue pair get the number of appearance and add
+    // (countA * countB * pairwiseScore(AB))
     // divide the meanscore by the sequence length afterwards
     float _meanscore = 0;
     for (char resA : seq1ResCount.keySet())
     {
       for (char resB : seq2ResCount.keySet())
       {
-       int countA = seq1ResCount.get(resA);
-       int countB = seq2ResCount.get(resB);
+        int countA = seq1ResCount.get(resA);
+        int countB = seq2ResCount.get(resB);
 
         float scoreAB = scoreMatrix.getPairwiseScore(resA, resB);
 
-       _meanscore += countA *  countB * scoreAB;
+        _meanscore += countA * countB * scoreAB;
       }
     }
     _meanscore /= length;
@@ -1407,21 +1420,23 @@ public class AlignSeq
   }
 
   /**
-  * calculate the hypothetic max score using the self-alignment of the sequences
-  */
+   * calculate the hypothetic max score using the self-alignment of the
+   * sequences
+   */
   public void hypotheticMaxScore()
   {
     int _hmsA = 0;
     int _hmsB = 0;
-    for (char residue: indelfreeAstr1.toCharArray())
+    for (char residue : indelfreeAstr1.toCharArray())
     {
       _hmsA += scoreMatrix.getPairwiseScore(residue, residue);
     }
-    for (char residue: indelfreeAstr2.toCharArray())
+    for (char residue : indelfreeAstr2.toCharArray())
     {
       _hmsB += scoreMatrix.getPairwiseScore(residue, residue);
     }
-    this.hypotheticMaxScore = (_hmsA < _hmsB) ? _hmsA : _hmsB; // take the lower self alignment
+    this.hypotheticMaxScore = (_hmsA < _hmsB) ? _hmsA : _hmsB; // take the lower
+                                                               // self alignment
 
   }
 
@@ -1431,27 +1446,30 @@ public class AlignSeq
   }
 
   /**
-  * create strings based of astr1 and astr2 but without gaps
-  */
+   * create strings based of astr1 and astr2 but without gaps
+   */
   public void getIndelfreeAstr()
   {
-    int n = astr1.length();    // both have the same length
+    int n = astr1.length(); // both have the same length
     for (int i = 0; i < n; i++)
     {
-      if (Character.isLetter(astr1.charAt(i)) && Character.isLetter(astr2.charAt(i)))  // if both sequences dont have a gap -> add to indelfreeAstr
+      if (Character.isLetter(astr1.charAt(i))
+              && Character.isLetter(astr2.charAt(i))) // if both sequences dont
+                                                      // have a gap -> add to
+                                                      // indelfreeAstr
       {
-       this.indelfreeAstr1 += astr1.charAt(i);
-       this.indelfreeAstr2 += astr2.charAt(i);
+        this.indelfreeAstr1 += astr1.charAt(i);
+        this.indelfreeAstr2 += astr2.charAt(i);
       }
     }
   }
 
   /**
-  * calculates the overall score of the alignment
-  * preprescore = sum of all scores - all penalties
-  * if preprescore < 1 ~ alignmentScore = Float.NaN    >
-  * alignmentScore = ((preprescore - meanScore) / (hypotheticMaxScore - meanScore)) * coverage
-  */
+   * calculates the overall score of the alignment preprescore = sum of all
+   * scores - all penalties if preprescore < 1 ~ alignmentScore = Float.NaN >
+   * alignmentScore = ((preprescore - meanScore) / (hypotheticMaxScore -
+   * meanScore)) * coverage
+   */
   public void scoreAlignment()
   {
 
@@ -1475,36 +1493,51 @@ public class AlignSeq
       char char2 = indelfreeAstr2.charAt(i);
       boolean aIsLetter = Character.isLetter(char1);
       boolean bIsLetter = Character.isLetter(char2);
-      if (aIsLetter && bIsLetter)      // if pair -> get score
+      if (aIsLetter && bIsLetter) // if pair -> get score
       {
         score += scoreMatrix.getPairwiseScore(char1, char2);
-      } else if (!aIsLetter && !bIsLetter) {   // both are gap -> skip
-      } else if ((!aIsLetter && aGapOpen) || (!bIsLetter && bGapOpen)) {       // one side gapopen -> score - gap_extend
-       score -= GAP_EXTEND_COST;
-      } else {         // no gap open -> score - gap_open
-       score -= GAP_OPEN_COST;
+      }
+      else if (!aIsLetter && !bIsLetter)
+      { // both are gap -> skip
+      }
+      else if ((!aIsLetter && aGapOpen) || (!bIsLetter && bGapOpen))
+      { // one side gapopen -> score - gap_extend
+        score -= GAP_EXTEND_COST;
+      }
+      else
+      { // no gap open -> score - gap_open
+        score -= GAP_OPEN_COST;
       }
       // adjust GapOpen status in both sequences
       aGapOpen = (!aIsLetter) ? true : false;
       bGapOpen = (!bIsLetter) ? true : false;
     }
 
-    float preprescore = score; // if this score < 1 --> alignment score = Float.NaN
-    score = (score - this.meanScore) / (this.hypotheticMaxScore - this.meanScore);
-    int[] _max = MiscMath.findMax(new int[]{astr1.replace("-","").length(), astr2.replace("-","").length()});  // {index of max, max}
-    float coverage = (float) n / (float) _max[1];      // indelfreeAstr length / longest sequence length
-    float prescore = score;    // only debug
+    float preprescore = score; // if this score < 1 --> alignment score =
+                               // Float.NaN
+    score = (score - this.meanScore)
+            / (this.hypotheticMaxScore - this.meanScore);
+    int[] _max = MiscMath
+            .findMax(new int[]
+            { astr1.replace("-", "").length(),
+                astr2.replace("-", "").length() }); // {index of max, max}
+    float coverage = (float) n / (float) _max[1]; // indelfreeAstr length /
+                                                  // longest sequence length
+    float prescore = score; // only debug
     score *= coverage;
 
-    //System.out.println(String.format("prepre-score: %f, pre-score: %f, longlength: %d\nscore: %1.16f, mean: %f, max: %d", preprescore, prescore, _max[1], score, this.meanScore, this.hypotheticMaxScore));
+    // System.out.println(String.format("prepre-score: %f, pre-score: %f,
+    // longlength: %d\nscore: %1.16f, mean: %f, max: %d", preprescore, prescore,
+    // _max[1], score, this.meanScore, this.hypotheticMaxScore));
     float minScore = 0f;
     this.alignmentScore = (score <= minScore) ? Float.NaN : score;
   }
 
   public void setScoreMatrix(ScoreMatrix sm)
   {
-       if (sm != null) {
-               scoreMatrix = sm;
-       }
+    if (sm != null)
+    {
+      scoreMatrix = sm;
+    }
   }
 }
index d78bcdc..4d2b1aa 100644 (file)
@@ -84,7 +84,7 @@ public class AlignmentUtils
 {
   private static final int CODON_LENGTH = 3;
 
-  private static final String SEQUENCE_VARIANT = "sequence_variant:"; 
+  private static final String SEQUENCE_VARIANT = "sequence_variant:";
 
   /*
    * the 'id' attribute is provided for variant features fetched from
@@ -1542,13 +1542,15 @@ public class AlignmentUtils
       }
     }
   }
-  
-  
-  public static boolean isSSAnnotationPresent( Map<SequenceI, List<AlignmentAnnotation>> annotations) {
-    
+
+  public static boolean isSSAnnotationPresent(
+          Map<SequenceI, List<AlignmentAnnotation>> annotations)
+  {
+
     for (SequenceI seq : annotations.keySet())
     {
-      if(isSecondaryStructurePresent(annotations.get(seq).toArray(new AlignmentAnnotation[0])))
+      if (isSecondaryStructurePresent(
+              annotations.get(seq).toArray(new AlignmentAnnotation[0])))
       {
         return true;
       }
@@ -2920,7 +2922,7 @@ public class AlignmentUtils
     return ssPresent;
 
   }
-  
+
   public static Color getSecondaryStructureAnnotationColour(char symbol)
   {
 
@@ -2938,10 +2940,11 @@ public class AlignmentUtils
     }
 
     return Color.white;
+
   }
 
-  public static char findSSAnnotationForGivenSeqposition(AlignmentAnnotation aa,
-          int seqPosition)
+  public static char findSSAnnotationForGivenSeqposition(
+          AlignmentAnnotation aa, int seqPosition)
   {
     char ss = '*';
 
@@ -3187,5 +3190,32 @@ public class AlignmentUtils
     return ssAlignmentAnnotationForSequences;
 
   }
-  
+
+
+  // to do set priority for labels
+  public static AlignmentAnnotation getDisplayedAlignmentAnnotation(
+          SequenceI seq)
+  {
+
+    for (String ssLabel : Constants.SECONDARY_STRUCTURE_LABELS.keySet())
+    {
+
+      AlignmentAnnotation[] aa = seq.getAnnotation(ssLabel);
+      if (aa != null)
+      {
+
+        for (AlignmentAnnotation annot : aa)
+        {
+          if (annot.visible)
+          {
+            return annot;
+          }
+        }
+      }
+    }
+
+    return null;
+
+  }
+
 }
index 9915f2a..5f371c8 100644 (file)
@@ -23,7 +23,9 @@ package jalview.analysis;
 public class ConnectivityException extends RuntimeException
 {
   private String sequence;
+
   private int connection;
+
   private byte dim;
 
   public ConnectivityException(String sequence, int connection, byte dim)
@@ -31,9 +33,11 @@ public class ConnectivityException extends RuntimeException
     this("Insufficient number of connections", sequence, connection, dim);
   }
 
-  public ConnectivityException(String message, String sequence, int connection, byte dim)
+  public ConnectivityException(String message, String sequence,
+          int connection, byte dim)
   {
-    super(String.format("%s for %s (%d, should be %d or more)", message, sequence, connection, dim));
+    super(String.format("%s for %s (%d, should be %d or more)", message,
+            sequence, connection, dim));
     this.sequence = sequence;
     this.connection = connection;
     this.dim = dim;
index c84c69a..32d4cf3 100644 (file)
@@ -606,7 +606,7 @@ public class Finder implements FinderI
     }
     else
     {
-      //allFeatures = sf.getAllFeatures(null);
+      // allFeatures = sf.getAllFeatures(null);
       allFeatures = sf.getAllFeatures();
     }
     // so we can check we are advancing when debugging
index 48ec899..5be8c7b 100755 (executable)
@@ -38,7 +38,8 @@ import java.util.Hashtable;
 
 /**
  * Performs Principal Component Analysis on given sequences
- * @AUTHOR MorellThomas 
+ * 
+ * @AUTHOR MorellThomas
  */
 public class PaSiMap implements Runnable
 {
@@ -72,14 +73,17 @@ public class PaSiMap implements Runnable
    * @param sm
    * @param options
    */
-  public PaSiMap(AlignmentViewport sequences, ScoreModelI sm, PairwiseAlignPanel pap)
+  public PaSiMap(AlignmentViewport sequences, ScoreModelI sm,
+          PairwiseAlignPanel pap)
   {
     this.seqs = sequences;
 
-    if (sm!=null && sm instanceof ScoreMatrix)
+    if (sm != null && sm instanceof ScoreMatrix)
     {
       this.scoreMatrix = ((ScoreMatrix) sm);
-    } else {
+    }
+    else
+    {
       this.scoreMatrix = null;
     }
 
@@ -108,7 +112,8 @@ public class PaSiMap implements Runnable
    *          DOCUMENT ME!
    * @param mm
    *          DOCUMENT ME!
-   * @param factor ~ is 1
+   * @param factor
+   *          ~ is 1
    * 
    * @return DOCUMENT ME!
    */
@@ -164,8 +169,8 @@ public class PaSiMap implements Runnable
   }
 
   /**
-   * Answers a formatted text report of the PaSiMap calculation results (matrices
-   * and eigenvalues) suitable for display
+   * Answers a formatted text report of the PaSiMap calculation results
+   * (matrices and eigenvalues) suitable for display
    * 
    * @return
    */
@@ -197,13 +202,25 @@ public class PaSiMap implements Runnable
   /**
    * Performs the PaSiMap calculation
    *
-   * creates a new gui/PairwiseAlignPanel with the input sequences (AlignmentViewport)
-   * uses analysis/AlignSeq to creatue the pairwise alignments and calculate the AlignmentScores (float for each pair)
+   * creates a new gui/PairwiseAlignPanel with the input sequences
+   * (AlignmentViewport)
+   * 
+   * uses analysis/AlignSeq to creatue the pairwise alignments and calculate the
+   * AlignmentScores (float for each pair)
+   * 
    * gets all float[][] scores from the gui/PairwiseAlignPanel
-   * checks the connections for each sequence with AlignmentViewport seqs.calculateConnectivity(float[][] scores, int dim) (from analysis/Connectivity) -- throws an Exception if insufficient
+   * 
+   * checks the connections for each sequence with AlignmentViewport
+   * seqs.calculateConnectivity(float[][] scores, int dim) (from
+   * analysis/Connectivity) -- throws an Exception if insufficient
+   * 
    * creates a math/MatrixI pairwiseScores of the float[][] scores
-   * copys the scores and fills the diagonal to create a symmetric matrix using math/Matrix.fillDiagonal()
+   * 
+   * copys the scores and fills the diagonal to create a symmetric matrix using
+   * math/Matrix.fillDiagonal()
+   * 
    * performs the analysis/ccAnalysis with the symmetric matrix
+   * 
    * gets the eigenmatrix and the eigenvalues using math/Matrix.tqli()
    */
   @Override
@@ -211,9 +228,10 @@ public class PaSiMap implements Runnable
   {
     try
     {
-      //alignment = new PairwiseAlignPanel(seqs, true, 100, 5);
+      // alignment = new PairwiseAlignPanel(seqs, true, 100, 5);
       alignment.calculate(scoreMatrix);
-      float[][] scores = alignment.getAlignmentScores();       //bigger index first -- eg scores[14][13]
+      float[][] scores = alignment.getAlignmentScores(); // bigger index first
+                                                         // -- eg scores[14][13]
       SequenceI[] iseqs = alignment.getInputSequences();
       Connectivity.getConnectivity(iseqs, scores, dim);
 
index 4922a54..62e1913 100755 (executable)
@@ -55,11 +55,11 @@ import org.apache.commons.math3.linear.SingularValueDecomposition;
 /**
  * A class to model rectangular matrices of double values and operations on them
  */
-public class ccAnalysis 
+public class ccAnalysis
 {
-  private byte dim = 0;                //dimensions
+  private byte dim = 0; // dimensions
 
-  private MatrixI scoresOld;   //input scores
+  private MatrixI scoresOld; // input scores
 
   public ccAnalysis(MatrixI scores, byte dim)
   {
@@ -68,29 +68,33 @@ public class ccAnalysis
     {
       for (int j = 0; j < scores.width(); j++)
       {
-       if (!Double.isNaN(scores.getValue(i,j)))
-       {
-         scores.setValue(i, j, (double) Math.round(scores.getValue(i,j) * (int) 10000) / 10000);
-       }
+        if (!Double.isNaN(scores.getValue(i, j)))
+        {
+          scores.setValue(i, j,
+                  (double) Math.round(scores.getValue(i, j) * (int) 10000)
+                          / 10000);
+        }
       }
     }
     this.scoresOld = scores;
     this.dim = dim;
   }
 
-  /** 
-  * Initialise a distrust-score for each hypothesis (h) of hSigns
-  * distrust = conHypNum - proHypNum
-  *
-  * @param hSigns ~ hypothesis signs (+/-) for each sequence
-  * @param scores ~ input score matrix
-  *
-  * @return distrustScores
-  */
+  /**
+   * Initialise a distrust-score for each hypothesis (h) of hSigns distrust =
+   * conHypNum - proHypNum
+   *
+   * @param hSigns
+   *          ~ hypothesis signs (+/-) for each sequence
+   * @param scores
+   *          ~ input score matrix
+   *
+   * @return distrustScores
+   */
   private int[] initialiseDistrusts(byte[] hSigns, MatrixI scores)
   {
     int[] distrustScores = new int[scores.width()];
-    
+
     // loop over symmetric matrix
     for (int i = 0; i < scores.width(); i++)
     {
@@ -100,34 +104,43 @@ public class ccAnalysis
 
       for (int j = 0; j < scores.width(); j++)
       {
-       double cell = scores.getRow(i)[j];      // value at [i][j] in scores
-       byte hBSign = hSigns[j];
-       if (!Double.isNaN(cell))
-       {
-         byte cellSign = (byte) Math.signum(cell);     //check if sign of matrix value fits hyptohesis
-         if (cellSign == hASign * hBSign)
-         {
-           proHypNum++;
-         } else {
-           conHypNum++;
-         }
-       }
+        double cell = scores.getRow(i)[j]; // value at [i][j] in scores
+        byte hBSign = hSigns[j];
+        if (!Double.isNaN(cell))
+        {
+          byte cellSign = (byte) Math.signum(cell); // check if sign of matrix
+                                                    // value fits hyptohesis
+          if (cellSign == hASign * hBSign)
+          {
+            proHypNum++;
+          }
+          else
+          {
+            conHypNum++;
+          }
+        }
       }
-      distrustScores[i] = conHypNum - proHypNum;       //create distrust score for each sequence
+      distrustScores[i] = conHypNum - proHypNum; // create distrust score for
+                                                 // each sequence
     }
     return distrustScores;
   }
 
   /**
-  * Optemise hypothesis concerning the sign of the hypothetical value for each hSigns by interpreting the pairwise correlation coefficients as scalar products
-  *
-  * @param hSigns ~ hypothesis signs (+/-)
-  * @param distrustScores
-  * @param scores ~ input score matrix
-  *
-  * @return hSigns
-  */
-  private byte[] optimiseHypothesis(byte[] hSigns, int[] distrustScores, MatrixI scores)
+   * Optimise hypothesis concerning the sign of the hypothetical value for each
+   * hSigns by interpreting the pairwise correlation coefficients as scalar
+   * products
+   *
+   * @param hSigns
+   *          ~ hypothesis signs (+/-)
+   * @param distrustScores
+   * @param scores
+   *          ~ input score matrix
+   *
+   * @return hSigns
+   */
+  private byte[] optimiseHypothesis(byte[] hSigns, int[] distrustScores,
+          MatrixI scores)
   {
     // get maximum distrust score
     int[] maxes = MiscMath.findMax(distrustScores);
@@ -137,7 +150,7 @@ public class ccAnalysis
     // if hypothesis is not optimal yet
     if (maxDistrust > 0)
     {
-      //toggle sign for hI with maximum distrust
+      // toggle sign for hI with maximum distrust
       hSigns[maxDistrustIndex] *= -1;
       // update distrust at same position
       distrustScores[maxDistrustIndex] *= -1;
@@ -146,299 +159,327 @@ public class ccAnalysis
       byte hASign = hSigns[maxDistrustIndex];
       for (int NOTmaxDistrustIndex = 0; NOTmaxDistrustIndex < distrustScores.length; NOTmaxDistrustIndex++)
       {
-       if (NOTmaxDistrustIndex != maxDistrustIndex)
-       {
-         byte hBSign = hSigns[NOTmaxDistrustIndex];
-         double cell = scores.getValue(maxDistrustIndex, NOTmaxDistrustIndex);
-
-         // distrust only changed if not NaN
-         if (!Double.isNaN(cell))
-         {
-           byte cellSign = (byte) Math.signum(cell);
-           // if sign of cell matches hypothesis decrease distrust by 2 because 1 more value supporting and 1 less contradicting
-           // else increase by 2
-           if (cellSign == hASign * hBSign)
-           {
-             distrustScores[NOTmaxDistrustIndex] -= 2;
-           } else {
-             distrustScores[NOTmaxDistrustIndex] += 2;
-           }
-         }
-       }
+        if (NOTmaxDistrustIndex != maxDistrustIndex)
+        {
+          byte hBSign = hSigns[NOTmaxDistrustIndex];
+          double cell = scores.getValue(maxDistrustIndex,
+                  NOTmaxDistrustIndex);
+
+          // distrust only changed if not NaN
+          if (!Double.isNaN(cell))
+          {
+            byte cellSign = (byte) Math.signum(cell);
+            // if sign of cell matches hypothesis decrease distrust by 2 because
+            // 1 more value supporting and 1 less contradicting
+            // else increase by 2
+            if (cellSign == hASign * hBSign)
+            {
+              distrustScores[NOTmaxDistrustIndex] -= 2;
+            }
+            else
+            {
+              distrustScores[NOTmaxDistrustIndex] += 2;
+            }
+          }
+        }
       }
-      //further optimisation necessary
+      // further optimisation necessary
       return optimiseHypothesis(hSigns, distrustScores, scores);
 
-    } else {
+    }
+    else
+    {
       return hSigns;
     }
   }
 
-  /** 
-  * takes the a symmetric MatrixI as input scores which may contain Double.NaN 
-  * approximate the missing values using hypothesis optimisation 
-  *
-  * runs analysis
-  *
-  * @param scores ~ score matrix
-  *
-  * @return
-  */
-  public MatrixI run () throws Exception
+  /**
+   * takes the a symmetric MatrixI as input scores which may contain Double.NaN
+   * approximate the missing values using hypothesis optimisation
+   *
+   * runs analysis
+   *
+   * @param scores
+   *          ~ score matrix
+   *
+   * @return
+   */
+  public MatrixI run() throws Exception
   {
-    //initialse eigenMatrix and repMatrix
+    // initialse eigenMatrix and repMatrix
     MatrixI eigenMatrix = scoresOld.copy();
     MatrixI repMatrix = scoresOld.copy();
     try
     {
-    /*
-    * Calculate correction factor for 2nd and higher eigenvalue(s).
-    * This correction is NOT needed for the 1st eigenvalue, because the
-    * unknown (=NaN) values of the matrix are approximated by presuming
-    * 1-dimensional vectors as the basis of the matrix interpretation as dot
-    * products.
-    */
-        
-    System.out.println("Input correlation matrix:");
-    eigenMatrix.print(System.out, "%1.4f ");
-
-    int matrixWidth = eigenMatrix.width(); // square matrix, so width == height
-    int matrixElementsTotal = (int) Math.pow(matrixWidth, 2);  //total number of elemts
-
-    float correctionFactor = (float) (matrixElementsTotal - eigenMatrix.countNaN()) / (float) matrixElementsTotal;
-    
-    /*
-    * Calculate hypothetical value (1-dimensional vector) h_i for each
-    * dataset by interpreting the given correlation coefficients as scalar
-    * products.
-    */
-
-    /*
-    * Memory for current hypothesis concerning sign of each h_i.
-    * List of signs for all h_i in the encoding:
+      /*
+      * Calculate correction factor for 2nd and higher eigenvalue(s).
+      * This correction is NOT needed for the 1st eigenvalue, because the
+      * unknown (=NaN) values of the matrix are approximated by presuming
+      * 1-dimensional vectors as the basis of the matrix interpretation as dot
+      * products.
+      */
+
+      System.out.println("Input correlation matrix:");
+      eigenMatrix.print(System.out, "%1.4f ");
+
+      int matrixWidth = eigenMatrix.width(); // square matrix, so width ==
+                                             // height
+      int matrixElementsTotal = (int) Math.pow(matrixWidth, 2); // total number
+                                                                // of elemts
+
+      float correctionFactor = (float) (matrixElementsTotal
+              - eigenMatrix.countNaN()) / (float) matrixElementsTotal;
+
+      /*
+      * Calculate hypothetical value (1-dimensional vector) h_i for each
+      * dataset by interpreting the given correlation coefficients as scalar
+      * products.
+      */
+
+      /*
+      * Memory for current hypothesis concerning sign of each h_i.
+      * List of signs for all h_i in the encoding:
       * *  1: positive
       * *  0: zero
       * * -1: negative
-    * Initial hypothesis: all signs are positive.
-    */
-    byte[] hSigns = new byte[matrixWidth];
-    Arrays.fill(hSigns, (byte) 1);
-
-    //Estimate signs for each h_i by refining hypothesis on signs.
-    hSigns = optimiseHypothesis(hSigns, initialiseDistrusts(hSigns, eigenMatrix), eigenMatrix);
-
-
-    //Estimate absolute values for each h_i by determining sqrt of mean of
-    //non-NaN absolute values for every row.
-    double[] hAbs = MiscMath.sqrt(eigenMatrix.absolute().meanRow());
-
-    //Combine estimated signs with absolute values in obtain total value for
-    //each h_i.
-    double[] hValues = MiscMath.elementwiseMultiply(hSigns, hAbs);
-
-    /*Complement symmetric matrix by using the scalar products of estimated
-    *values of h_i to replace NaN-cells.
-    *Matrix positions that have estimated values
-    *(only for diagonal and upper off-diagonal values, due to the symmetry
-    *the positions of the lower-diagonal values can be inferred).
-    List of tuples (row_idx, column_idx).*/
-
-    ArrayList<int[]> estimatedPositions = new ArrayList<int[]>();
-
-    // for off-diagonal cells
-    for (int rowIndex = 0; rowIndex < matrixWidth - 1; rowIndex++)
-    {
-      for (int columnIndex = rowIndex + 1; columnIndex < matrixWidth; columnIndex++)
+      * Initial hypothesis: all signs are positive.
+      */
+      byte[] hSigns = new byte[matrixWidth];
+      Arrays.fill(hSigns, (byte) 1);
+
+      // Estimate signs for each h_i by refining hypothesis on signs.
+      hSigns = optimiseHypothesis(hSigns,
+              initialiseDistrusts(hSigns, eigenMatrix), eigenMatrix);
+
+      // Estimate absolute values for each h_i by determining sqrt of mean of
+      // non-NaN absolute values for every row.
+      double[] hAbs = MiscMath.sqrt(eigenMatrix.absolute().meanRow());
+
+      // Combine estimated signs with absolute values in obtain total value for
+      // each h_i.
+      double[] hValues = MiscMath.elementwiseMultiply(hSigns, hAbs);
+
+      /*Complement symmetric matrix by using the scalar products of estimated
+      *values of h_i to replace NaN-cells.
+      *Matrix positions that have estimated values
+      *(only for diagonal and upper off-diagonal values, due to the symmetry
+      *the positions of the lower-diagonal values can be inferred).
+      List of tuples (row_idx, column_idx).*/
+
+      ArrayList<int[]> estimatedPositions = new ArrayList<int[]>();
+
+      // for off-diagonal cells
+      for (int rowIndex = 0; rowIndex < matrixWidth - 1; rowIndex++)
       {
-       double cell = eigenMatrix.getValue(rowIndex, columnIndex);
-       if (Double.isNaN(cell))
-       {
-         //calculate scalar product as new cell value
-         cell = hValues[rowIndex] * hValues[columnIndex];
-          //fill in new value in cell and symmetric partner
-         eigenMatrix.setValue(rowIndex, columnIndex, cell);
-         eigenMatrix.setValue(columnIndex, rowIndex, cell);
-         //save positions of estimated values
-         estimatedPositions.add(new int[]{rowIndex, columnIndex});
-       }
+        for (int columnIndex = rowIndex
+                + 1; columnIndex < matrixWidth; columnIndex++)
+        {
+          double cell = eigenMatrix.getValue(rowIndex, columnIndex);
+          if (Double.isNaN(cell))
+          {
+            // calculate scalar product as new cell value
+            cell = hValues[rowIndex] * hValues[columnIndex];
+            // fill in new value in cell and symmetric partner
+            eigenMatrix.setValue(rowIndex, columnIndex, cell);
+            eigenMatrix.setValue(columnIndex, rowIndex, cell);
+            // save positions of estimated values
+            estimatedPositions.add(new int[] { rowIndex, columnIndex });
+          }
+        }
       }
-    }
 
-    // for diagonal cells
-    for (int diagonalIndex = 0; diagonalIndex < matrixWidth; diagonalIndex++)
+      // for diagonal cells
+      for (int diagonalIndex = 0; diagonalIndex < matrixWidth; diagonalIndex++)
       {
         double cell = Math.pow(hValues[diagonalIndex], 2);
-       eigenMatrix.setValue(diagonalIndex, diagonalIndex, cell);
-       estimatedPositions.add(new int[]{diagonalIndex, diagonalIndex});
+        eigenMatrix.setValue(diagonalIndex, diagonalIndex, cell);
+        estimatedPositions.add(new int[] { diagonalIndex, diagonalIndex });
       }
 
-    /*Refine total values of each h_i:
-    *Initialise h_values of the hypothetical non-existant previous iteration
-    *with the correct format but with impossible values.
-     Needed for exit condition of otherwise endless loop.*/
-    System.out.print("initial values: [ ");
-    for (double h : hValues)
-    {
-      System.out.print(String.format("%1.4f, ", h));
-    }
-    System.out.println(" ]");
-
-
-    double[] hValuesOld = new double[matrixWidth];
-
-    int iterationCount = 0;
-
-    // repeat unitl values of h do not significantly change anymore
-    while (true)
-    {
-      for (int hIndex = 0; hIndex < matrixWidth; hIndex++)
-      {
-       double newH = Arrays.stream(MiscMath.elementwiseMultiply(hValues, eigenMatrix.getRow(hIndex))).sum() / Arrays.stream(MiscMath.elementwiseMultiply(hValues, hValues)).sum();
-       hValues[hIndex] = newH;
-      }
-
-      System.out.print(String.format("iteration %d: [ ", iterationCount));
+      /*Refine total values of each h_i:
+      *Initialise h_values of the hypothetical non-existant previous iteration
+      *with the correct format but with impossible values.
+       Needed for exit condition of otherwise endless loop.*/
+      System.out.print("initial values: [ ");
       for (double h : hValues)
       {
-       System.out.print(String.format("%1.4f, ", h));
+        System.out.print(String.format("%1.4f, ", h));
       }
       System.out.println(" ]");
 
-      //update values of estimated positions
-      for (int[] pair : estimatedPositions)    // pair ~ row, col
-      {
-        double newVal = hValues[pair[0]] * hValues[pair[1]];
-       eigenMatrix.setValue(pair[0], pair[1], newVal);
-       eigenMatrix.setValue(pair[1], pair[0], newVal);
-      }
+      double[] hValuesOld = new double[matrixWidth];
 
-      iterationCount++;
+      int iterationCount = 0;
 
-      //exit loop as soon as new values are similar to the last iteration
-      if (MiscMath.allClose(hValues, hValuesOld, 0d, 1e-05d, false))
+      // FIXME JAL-4443 - spliterators could be coded out or patched with j2s
+      // annotation
+      // repeat unitl values of h do not significantly change anymore
+      while (true)
       {
-        break;
+        for (int hIndex = 0; hIndex < matrixWidth; hIndex++)
+        {
+          double newH = Arrays
+                  .stream(MiscMath.elementwiseMultiply(hValues,
+                          eigenMatrix.getRow(hIndex)))
+                  .sum()
+                  / Arrays.stream(
+                          MiscMath.elementwiseMultiply(hValues, hValues))
+                          .sum();
+          hValues[hIndex] = newH;
+        }
+
+        System.out.print(String.format("iteration %d: [ ", iterationCount));
+        for (double h : hValues)
+        {
+          System.out.print(String.format("%1.4f, ", h));
+        }
+        System.out.println(" ]");
+
+        // update values of estimated positions
+        for (int[] pair : estimatedPositions) // pair ~ row, col
+        {
+          double newVal = hValues[pair[0]] * hValues[pair[1]];
+          eigenMatrix.setValue(pair[0], pair[1], newVal);
+          eigenMatrix.setValue(pair[1], pair[0], newVal);
+        }
+
+        iterationCount++;
+
+        // exit loop as soon as new values are similar to the last iteration
+        if (MiscMath.allClose(hValues, hValuesOld, 0d, 1e-05d, false))
+        {
+          break;
+        }
+
+        // save hValues for comparison in the next iteration
+        System.arraycopy(hValues, 0, hValuesOld, 0, hValues.length);
       }
 
-      //save hValues for comparison in the next iteration
-      System.arraycopy(hValues, 0, hValuesOld, 0, hValues.length);
-    }
-
-    //-----------------------------
-    //Use complemented symmetric matrix to calculate final representative
-    //vectors.
-
-    //Eigendecomposition.
-    eigenMatrix.tred();
-    eigenMatrix.tqli();
+      // -----------------------------
+      // Use complemented symmetric matrix to calculate final representative
+      // vectors.
 
-    System.out.println("eigenmatrix");
-    eigenMatrix.print(System.out, "%8.2f");
-    System.out.println();
-    System.out.println("uncorrected eigenvalues");
-    eigenMatrix.printD(System.out, "%2.4f ");
-    System.out.println();
+      // Eigendecomposition.
+      eigenMatrix.tred();
+      eigenMatrix.tqli();
 
-    double[] eigenVals = eigenMatrix.getD();
+      System.out.println("eigenmatrix");
+      eigenMatrix.print(System.out, "%8.2f");
+      System.out.println();
+      System.out.println("uncorrected eigenvalues");
+      eigenMatrix.printD(System.out, "%2.4f ");
+      System.out.println();
 
-    TreeMap<Double, Integer> eigenPairs = new TreeMap<>(Comparator.reverseOrder());
-    for (int i = 0; i < eigenVals.length; i++)
-    {
-      eigenPairs.put(eigenVals[i], i);
-    }
+      double[] eigenVals = eigenMatrix.getD();
 
-    // matrix of representative eigenvectors (each row is a vector)
-    double[][] _repMatrix = new double[eigenVals.length][dim];
-    double[][] _oldMatrix = new double[eigenVals.length][dim];
-    double[] correctedEigenValues = new double[dim];   
-
-    int l = 0;
-    for (Entry<Double, Integer> pair : eigenPairs.entrySet())
-    {
-      double eigenValue = pair.getKey();
-      int column = pair.getValue();
-      double[] eigenVector = eigenMatrix.getColumn(column);
-      //for 2nd and higher eigenvalues
-      if (l >= 1)
+      TreeMap<Double, Integer> eigenPairs = new TreeMap<>(
+              Comparator.reverseOrder());
+      for (int i = 0; i < eigenVals.length; i++)
       {
-        eigenValue /= correctionFactor;
+        eigenPairs.put(eigenVals[i], i);
       }
-      correctedEigenValues[l] = eigenValue;
-      for (int j = 0; j < eigenVector.length; j++)
-      {
-       _repMatrix[j][l] = (eigenValue < 0) ? 0.0 : - Math.sqrt(eigenValue) * eigenVector[j];
-       double tmpOldScore = scoresOld.getColumn(column)[j];
-       _oldMatrix[j][dim - l - 1] = (Double.isNaN(tmpOldScore)) ? 0.0 : tmpOldScore;
-      }
-      l++;
-      if (l >= dim)
-      {
-       break;
-      }
-    }
 
-    System.out.println("correctedEigenValues");
-    MiscMath.print(correctedEigenValues, "%2.4f ");
-
-    repMatrix = new Matrix(_repMatrix);
-    repMatrix.setD(correctedEigenValues);
-    MatrixI oldMatrix = new Matrix(_oldMatrix);
-
-    MatrixI dotMatrix = repMatrix.postMultiply(repMatrix.transpose());
-    
-    double rmsd = scoresOld.rmsd(dotMatrix);
-
-    System.out.println("iteration, rmsd, maxDiff, rmsdDiff");
-    System.out.println(String.format("0, %8.5f, -, -", rmsd));
-    // Refine representative vectors by minimising sum-of-squared deviates between dotMatrix and original  score matrix
-    for (int iteration = 1; iteration < 21; iteration++)       // arbitrarily set to 20
-    {
-      MatrixI repMatrixOLD = repMatrix.copy();
-      MatrixI dotMatrixOLD = dotMatrix.copy();
+      // matrix of representative eigenvectors (each row is a vector)
+      double[][] _repMatrix = new double[eigenVals.length][dim];
+      double[][] _oldMatrix = new double[eigenVals.length][dim];
+      double[] correctedEigenValues = new double[dim];
 
-      // for all rows/hA in the original matrix
-      for (int hAIndex = 0; hAIndex < oldMatrix.height(); hAIndex++)
+      int l = 0;
+      for (Entry<Double, Integer> pair : eigenPairs.entrySet())
       {
-       double[] row = oldMatrix.getRow(hAIndex);
-       double[] hA = repMatrix.getRow(hAIndex);
-       hAIndex = hAIndex;
-       //find least-squares-solution fo rdifferences between original scores and representative vectors
-       double[] hAlsm = leastSquaresOptimisation(repMatrix, scoresOld, hAIndex);
-        // update repMatrix with new hAlsm
-       for (int j = 0; j < repMatrix.width(); j++)
-       {
-         repMatrix.setValue(hAIndex, j, hAlsm[j]);
-       }
-      }
-      
-      // dot product of representative vecotrs yields a matrix with values approximating the correlation matrix
-      dotMatrix = repMatrix.postMultiply(repMatrix.transpose());
-      // calculate rmsd between approximation and correlation matrix
-      rmsd = scoresOld.rmsd(dotMatrix);
-
-      // calculate maximum change of representative vectors of current iteration
-      MatrixI diff = repMatrix.subtract(repMatrixOLD).absolute();
-      double maxDiff = 0.0;
-      for (int i = 0; i < diff.height(); i++)
-      {
-       for (int j = 0; j < diff.width(); j++)
-       {
-         maxDiff = (diff.getValue(i, j) > maxDiff) ? diff.getValue(i, j) : maxDiff;
-       }
+        double eigenValue = pair.getKey();
+        int column = pair.getValue();
+        double[] eigenVector = eigenMatrix.getColumn(column);
+        // for 2nd and higher eigenvalues
+        if (l >= 1)
+        {
+          eigenValue /= correctionFactor;
+        }
+        correctedEigenValues[l] = eigenValue;
+        for (int j = 0; j < eigenVector.length; j++)
+        {
+          _repMatrix[j][l] = (eigenValue < 0) ? 0.0
+                  : -Math.sqrt(eigenValue) * eigenVector[j];
+          double tmpOldScore = scoresOld.getColumn(column)[j];
+          _oldMatrix[j][dim - l - 1] = (Double.isNaN(tmpOldScore)) ? 0.0
+                  : tmpOldScore;
+        }
+        l++;
+        if (l >= dim)
+        {
+          break;
+        }
       }
 
-      // calculate rmsd between current and previous estimation
-      double rmsdDiff = dotMatrix.rmsd(dotMatrixOLD);
+      System.out.println("correctedEigenValues");
+      MiscMath.print(correctedEigenValues, "%2.4f ");
+
+      repMatrix = new Matrix(_repMatrix);
+      repMatrix.setD(correctedEigenValues);
+      MatrixI oldMatrix = new Matrix(_oldMatrix);
 
-      System.out.println(String.format("%d, %8.5f, %8.5f, %8.5f", iteration, rmsd, maxDiff, rmsdDiff));
+      MatrixI dotMatrix = repMatrix.postMultiply(repMatrix.transpose());
 
-      if (!(Math.abs(maxDiff) > 1e-06))
+      double rmsd = scoresOld.rmsd(dotMatrix);
+
+      System.out.println("iteration, rmsd, maxDiff, rmsdDiff");
+      System.out.println(String.format("0, %8.5f, -, -", rmsd));
+      // Refine representative vectors by minimising sum-of-squared deviates
+      // between dotMatrix and original score matrix
+      for (int iteration = 1; iteration < 21; iteration++) // arbitrarily set to
+                                                           // 20
       {
-       repMatrix = repMatrixOLD.copy();
-       break;
+        MatrixI repMatrixOLD = repMatrix.copy();
+        MatrixI dotMatrixOLD = dotMatrix.copy();
+
+        // for all rows/hA in the original matrix
+        for (int hAIndex = 0; hAIndex < oldMatrix.height(); hAIndex++)
+        {
+          double[] row = oldMatrix.getRow(hAIndex);
+          double[] hA = repMatrix.getRow(hAIndex);
+          hAIndex = hAIndex;
+          // find least-squares-solution fo rdifferences between original scores
+          // and representative vectors
+          double[] hAlsm = leastSquaresOptimisation(repMatrix, scoresOld,
+                  hAIndex);
+          // update repMatrix with new hAlsm
+          for (int j = 0; j < repMatrix.width(); j++)
+          {
+            repMatrix.setValue(hAIndex, j, hAlsm[j]);
+          }
+        }
+
+        // dot product of representative vecotrs yields a matrix with values
+        // approximating the correlation matrix
+        dotMatrix = repMatrix.postMultiply(repMatrix.transpose());
+        // calculate rmsd between approximation and correlation matrix
+        rmsd = scoresOld.rmsd(dotMatrix);
+
+        // calculate maximum change of representative vectors of current
+        // iteration
+        MatrixI diff = repMatrix.subtract(repMatrixOLD).absolute();
+        double maxDiff = 0.0;
+        for (int i = 0; i < diff.height(); i++)
+        {
+          for (int j = 0; j < diff.width(); j++)
+          {
+            maxDiff = (diff.getValue(i, j) > maxDiff) ? diff.getValue(i, j)
+                    : maxDiff;
+          }
+        }
+
+        // calculate rmsd between current and previous estimation
+        double rmsdDiff = dotMatrix.rmsd(dotMatrixOLD);
+
+        System.out.println(String.format("%d, %8.5f, %8.5f, %8.5f",
+                iteration, rmsd, maxDiff, rmsdDiff));
+
+        if (!(Math.abs(maxDiff) > 1e-06))
+        {
+          repMatrix = repMatrixOLD.copy();
+          break;
+        }
       }
-    }
-    
 
     } catch (Exception q)
     {
@@ -451,35 +492,48 @@ public class ccAnalysis
   }
 
   /**
-  * Create equations system using information on originally known
-  * pairwise correlation coefficients (parsed from infile) and the
-  * representative result vectors
-  *
-  * Each equation has the format:
-  * hA * hA - pairwiseCC = 0
-  * with:
-  * hA: unknown variable
-  * hB: known representative vector
-  * pairwiseCC: known pairwise correlation coefficien
-  * 
-  * The resulting equations system is overdetermined, if there are more
-  * equations than unknown elements
-  *
-  * @param x ~ unknown n-dimensional column-vector
-  * (needed for generating equations system, NOT to be specified by user).
-  * @param hAIndex ~ index of currently optimised representative result vector.
-  * @param h ~ matrix with row-wise listing of representative result vectors.
-  * @param originalRow ~ matrix-row of originally parsed pairwise correlation coefficients.
-  *
-  * @return
-  */
-  private double[] originalToEquasionSystem(double[] hA, MatrixI repMatrix, MatrixI scoresOld, int hAIndex)
+   * Create equations system using information on originally known pairwise
+   * correlation coefficients (parsed from infile) and the representative result
+   * vectors
+   *
+   * Each equation has the format:
+   * 
+   * hA * hA - pairwiseCC = 0
+   * 
+   * with:
+   * 
+   * hA: unknown variable
+   * 
+   * hB: known representative vector
+   * 
+   * pairwiseCC: known pairwise correlation coefficien
+   * 
+   * The resulting equations system is overdetermined, if there are more
+   * equations than unknown elements
+   *
+   * x is the user input. Remaining parameters are needed for generating
+   * equations system, NOT to be specified by user).
+   * 
+   * @param x
+   *          ~ unknown n-dimensional column-vector
+   * @param hAIndex
+   *          ~ index of currently optimised representative result vector.
+   * @param h
+   *          ~ matrix with row-wise listing of representative result vectors.
+   * @param originalRow
+   *          ~ matrix-row of originally parsed pairwise correlation
+   *          coefficients.
+   *
+   * @return
+   */
+  private double[] originalToEquasionSystem(double[] hA, MatrixI repMatrix,
+          MatrixI scoresOld, int hAIndex)
   {
     double[] originalRow = scoresOld.getRow(hAIndex);
     int nans = MiscMath.countNaN(originalRow);
     double[] result = new double[originalRow.length - nans];
 
-    //for all pairwiseCC in originalRow
+    // for all pairwiseCC in originalRow
     int resultIndex = 0;
     for (int hBIndex = 0; hBIndex < originalRow.length; hBIndex++)
     {
@@ -488,40 +542,48 @@ public class ccAnalysis
       if (!Double.isNaN(pairwiseCC))
       {
         double[] hB = repMatrix.getRow(hBIndex);
-        result[resultIndex++] = MiscMath.sum(MiscMath.elementwiseMultiply(hA, hB)) - pairwiseCC;
-      } else {
+        result[resultIndex++] = MiscMath
+                .sum(MiscMath.elementwiseMultiply(hA, hB)) - pairwiseCC;
+      }
+      else
+      {
       }
     }
     return result;
   }
 
   /**
-  * returns the jacobian matrix
-  * @param repMatrix ~ matrix of representative vectors
-  * @param hAIndex ~ current row index
-  *
-  * @return
-  */
-  private MatrixI approximateDerivative(MatrixI repMatrix, MatrixI scoresOld, int hAIndex)
+   * returns the jacobian matrix
+   * 
+   * @param repMatrix
+   *          ~ matrix of representative vectors
+   * @param hAIndex
+   *          ~ current row index
+   *
+   * @return
+   */
+  private MatrixI approximateDerivative(MatrixI repMatrix,
+          MatrixI scoresOld, int hAIndex)
   {
-    //hA = x0
+    // hA = x0
     double[] hA = repMatrix.getRow(hAIndex);
-    double[] f0 = originalToEquasionSystem(hA, repMatrix, scoresOld, hAIndex);
+    double[] f0 = originalToEquasionSystem(hA, repMatrix, scoresOld,
+            hAIndex);
     double[] signX0 = new double[hA.length];
     double[] xAbs = new double[hA.length];
     for (int i = 0; i < hA.length; i++)
     {
       signX0[i] = (hA[i] >= 0) ? 1 : -1;
       xAbs[i] = (Math.abs(hA[i]) >= 1.0) ? Math.abs(hA[i]) : 1.0;
-      }
+    }
     double rstep = Math.pow(Math.ulp(1.0), 0.5);
 
-    double[] h = new double [hA.length];
+    double[] h = new double[hA.length];
     for (int i = 0; i < hA.length; i++)
     {
       h[i] = rstep * signX0[i] * xAbs[i];
     }
-      
+
     int m = f0.length;
     int n = hA.length;
     double[][] jTransposed = new double[n][m];
@@ -531,11 +593,12 @@ public class ccAnalysis
       System.arraycopy(hA, 0, x, 0, h.length);
       x[i] += h[i];
       double dx = x[i] - hA[i];
-      double[] df = originalToEquasionSystem(x, repMatrix, scoresOld, hAIndex);
+      double[] df = originalToEquasionSystem(x, repMatrix, scoresOld,
+              hAIndex);
       for (int j = 0; j < df.length; j++)
       {
-       df[j] -= f0[j];
-       jTransposed[i][j] = df[j] / dx;
+        df[j] -= f0[j];
+        jTransposed[i][j] = df[j] / dx;
       }
     }
     MatrixI J = new Matrix(jTransposed).transpose();
@@ -543,31 +606,40 @@ public class ccAnalysis
   }
 
   /**
-  * norm of regularized (by alpha) least-squares solution minus Delta
-  * @param alpha
-  * @param suf
-  * @param s
-  * @param Delta
-  *
-  * @return
-  */
-  private double[] phiAndDerivative(double alpha, double[] suf, double[] s, double Delta)
+   * norm of regularized (by alpha) least-squares solution minus Delta
+   * 
+   * @param alpha
+   * @param suf
+   * @param s
+   * @param Delta
+   *
+   * @return
+   */
+  private double[] phiAndDerivative(double alpha, double[] suf, double[] s,
+          double Delta)
   {
-    double[] denom = MiscMath.elementwiseAdd(MiscMath.elementwiseMultiply(s, s), alpha);
+    double[] denom = MiscMath
+            .elementwiseAdd(MiscMath.elementwiseMultiply(s, s), alpha);
     double pNorm = MiscMath.norm(MiscMath.elementwiseDivide(suf, denom));
     double phi = pNorm - Delta;
     // - sum ( suf**2 / denom**3) / pNorm
-    double phiPrime = - MiscMath.sum(MiscMath.elementwiseDivide(MiscMath.elementwiseMultiply(suf, suf), MiscMath.elementwiseMultiply(MiscMath.elementwiseMultiply(denom, denom), denom))) / pNorm;
-    return new double[]{phi, phiPrime};
+    double phiPrime = -MiscMath.sum(MiscMath.elementwiseDivide(
+            MiscMath.elementwiseMultiply(suf, suf),
+            MiscMath.elementwiseMultiply(
+                    MiscMath.elementwiseMultiply(denom, denom), denom)))
+            / pNorm;
+    return new double[] { phi, phiPrime };
   }
 
   /**
-  * class holding the result of solveLsqTrustRegion
-  */
+   * class holding the result of solveLsqTrustRegion
+   */
   private class TrustRegion
   {
     private double[] step;
+
     private double alpha;
+
     private int iteration;
 
     public TrustRegion(double[] step, double alpha, int iteration)
@@ -586,7 +658,7 @@ public class ccAnalysis
     {
       return this.alpha;
     }
-  
+
     public int getIteration()
     {
       return this.iteration;
@@ -594,22 +666,30 @@ public class ccAnalysis
   }
 
   /**
-  * solve a trust-region problem arising in least-squares optimisation
-  * @param n ~ number of variables
-  * @param m ~ number of residuals
-  * @param uf
-  * @param s ~ singular values of J
-  * @param V ~ transpose of VT
-  * @param Delta ~ radius of a trust region
-  * @param alpha ~ initial guess for alpha
-  *
-  * @return
-  */
-  private TrustRegion solveLsqTrustRegion(int n, int m, double[] uf, double[] s, MatrixI V, double Delta, double alpha)
+   * solve a trust-region problem arising in least-squares optimisation
+   * 
+   * @param n
+   *          ~ number of variables
+   * @param m
+   *          ~ number of residuals
+   * @param uf
+   * @param s
+   *          ~ singular values of J
+   * @param V
+   *          ~ transpose of VT
+   * @param Delta
+   *          ~ radius of a trust region
+   * @param alpha
+   *          ~ initial guess for alpha
+   *
+   * @return
+   */
+  private TrustRegion solveLsqTrustRegion(int n, int m, double[] uf,
+          double[] s, MatrixI V, double Delta, double alpha)
   {
     double[] suf = MiscMath.elementwiseMultiply(s, uf);
 
-    //check if J has full rank and tr Gauss-Newton step
+    // check if J has full rank and tr Gauss-Newton step
     boolean fullRank = false;
     if (m >= n)
     {
@@ -618,7 +698,8 @@ public class ccAnalysis
     }
     if (fullRank)
     {
-      double[] p = MiscMath.elementwiseMultiply(V.sumProduct(MiscMath.elementwiseDivide(uf, s)), -1);
+      double[] p = MiscMath.elementwiseMultiply(
+              V.sumProduct(MiscMath.elementwiseDivide(uf, s)), -1);
       if (MiscMath.norm(p) <= Delta)
       {
         TrustRegion result = new TrustRegion(p, 0.0, 0);
@@ -631,15 +712,21 @@ public class ccAnalysis
     if (fullRank)
     {
       double[] phiAndPrime = phiAndDerivative(0.0, suf, s, Delta);
-      alphaLower = - phiAndPrime[0] / phiAndPrime[1];
+      alphaLower = -phiAndPrime[0] / phiAndPrime[1];
     }
 
-    alpha = (!fullRank && alpha == 0.0) ? alpha = Math.max(0.001 * alphaUpper, Math.pow(alphaLower * alphaUpper, 0.5)) : alpha;
+    alpha = (!fullRank && alpha == 0.0)
+            ? alpha = Math.max(0.001 * alphaUpper,
+                    Math.pow(alphaLower * alphaUpper, 0.5))
+            : alpha;
 
     int iteration = 0;
-    while (iteration < 10)     // 10 is default max_iter
+    while (iteration < 10) // 10 is default max_iter
     {
-      alpha = (alpha < alphaLower || alpha > alphaUpper) ? alpha = Math.max(0.001 * alphaUpper, Math.pow(alphaLower * alphaUpper, 0.5)) : alpha;
+      alpha = (alpha < alphaLower || alpha > alphaUpper)
+              ? alpha = Math.max(0.001 * alphaUpper,
+                      Math.pow(alphaLower * alphaUpper, 0.5))
+              : alpha;
       double[] phiAndPrime = phiAndDerivative(alpha, suf, s, Delta);
       double phi = phiAndPrime[0];
       double phiPrime = phiAndPrime[1];
@@ -649,18 +736,20 @@ public class ccAnalysis
       alphaLower = Math.max(alphaLower, alpha - ratio);
       alpha -= (phi + Delta) * ratio / Delta;
 
-      if (Math.abs(phi) < 0.01 * Delta)        // default rtol set to 0.01
+      if (Math.abs(phi) < 0.01 * Delta) // default rtol set to 0.01
       {
-       break;
+        break;
       }
       iteration++;
     }
 
     // p = - V.dot( suf / (s**2 + alpha))
-    double[] tmp = MiscMath.elementwiseDivide(suf, MiscMath.elementwiseAdd(MiscMath.elementwiseMultiply(s, s), alpha));
+    double[] tmp = MiscMath.elementwiseDivide(suf, MiscMath
+            .elementwiseAdd(MiscMath.elementwiseMultiply(s, s), alpha));
     double[] p = MiscMath.elementwiseMultiply(V.sumProduct(tmp), -1);
 
-    // Make the norm of p equal to Delta, p is changed only slightly during this.
+    // Make the norm of p equal to Delta, p is changed only slightly during
+    // this.
     // It is done to prevent p lie outside of the trust region
     p = MiscMath.elementwiseMultiply(p, Delta / MiscMath.norm(p));
 
@@ -669,15 +758,18 @@ public class ccAnalysis
   }
 
   /**
-  * compute values of a quadratic function arising in least squares
-  * function: 0.5 * s.T * (J.T * J + diag) * s + g.T * s
-  *
-  * @param J ~ jacobian matrix
-  * @param g ~ gradient
-  * @param s ~ steps and rows
-  *
-  * @return
-  */
+   * compute values of a quadratic function arising in least squares function:
+   * 0.5 * s.T * (J.T * J + diag) * s + g.T * s
+   *
+   * @param J
+   *          ~ jacobian matrix
+   * @param g
+   *          ~ gradient
+   * @param s
+   *          ~ steps and rows
+   *
+   * @return
+   */
   private double evaluateQuadratic(MatrixI J, double[] g, double[] s)
   {
 
@@ -689,50 +781,64 @@ public class ccAnalysis
   }
 
   /**
-  * update the radius of a trust region based on the cost reduction
-  *
-  * @param Delta
-  * @param actualReduction
-  * @param predictedReduction
-  * @param stepNorm
-  * @param boundHit
-  *
-  * @return
-  */
-  private double[] updateTrustRegionRadius(double Delta, double actualReduction, double predictedReduction, double stepNorm, boolean boundHit)
+   * update the radius of a trust region based on the cost reduction
+   *
+   * @param Delta
+   * @param actualReduction
+   * @param predictedReduction
+   * @param stepNorm
+   * @param boundHit
+   *
+   * @return
+   */
+  private double[] updateTrustRegionRadius(double Delta,
+          double actualReduction, double predictedReduction,
+          double stepNorm, boolean boundHit)
   {
     double ratio = 0;
     if (predictedReduction > 0)
     {
       ratio = actualReduction / predictedReduction;
-    } else if (predictedReduction == 0 && actualReduction == 0) {
+    }
+    else if (predictedReduction == 0 && actualReduction == 0)
+    {
       ratio = 1;
-    } else {
+    }
+    else
+    {
       ratio = 0;
     }
 
     if (ratio < 0.25)
     {
       Delta = 0.25 * stepNorm;
-    } else if (ratio > 0.75 && boundHit) {
+    }
+    else if (ratio > 0.75 && boundHit)
+    {
       Delta *= 2.0;
     }
 
-    return new double[]{Delta, ratio};
+    return new double[] { Delta, ratio };
   }
 
   /**
-  * trust region reflective algorithm
-  * @param repMatrix ~ Matrix containing representative vectors
-  * @param scoresOld ~ Matrix containing initial observations
-  * @param index ~ current row index
-  * @param J ~ jacobian matrix
-  *
-  * @return
-  */
-  private double[] trf(MatrixI repMatrix, MatrixI scoresOld, int index, MatrixI J)
+   * trust region reflective algorithm
+   * 
+   * @param repMatrix
+   *          ~ Matrix containing representative vectors
+   * @param scoresOld
+   *          ~ Matrix containing initial observations
+   * @param index
+   *          ~ current row index
+   * @param J
+   *          ~ jacobian matrix
+   *
+   * @return
+   */
+  private double[] trf(MatrixI repMatrix, MatrixI scoresOld, int index,
+          MatrixI J)
   {
-    //hA = x0
+    // hA = x0
     double[] hA = repMatrix.getRow(index);
     double[] f0 = originalToEquasionSystem(hA, repMatrix, scoresOld, index);
     int nfev = 1;
@@ -742,7 +848,7 @@ public class ccAnalysis
     double[] g = J.transpose().sumProduct(f0);
     double Delta = MiscMath.norm(hA);
     int maxNfev = hA.length * 100;
-    double alpha = 0.0;                // "Levenberg-Marquardt" parameter
+    double alpha = 0.0; // "Levenberg-Marquardt" parameter
 
     double gNorm = 0;
     boolean terminationStatus = false;
@@ -753,11 +859,12 @@ public class ccAnalysis
       gNorm = MiscMath.norm(g);
       if (terminationStatus || nfev == maxNfev)
       {
-       break;
+        break;
       }
-      SingularValueDecomposition svd = new SingularValueDecomposition(new Array2DRowRealMatrix(J.asArray()));
+      SingularValueDecomposition svd = new SingularValueDecomposition(
+              new Array2DRowRealMatrix(J.asArray()));
       MatrixI U = new Matrix(svd.getU().getData());
-      double[] s = svd.getSingularValues();    
+      double[] s = svd.getSingularValues();
       MatrixI V = new Matrix(svd.getV().getData()).transpose();
       double[] uf = U.transpose().sumProduct(f0);
 
@@ -766,60 +873,67 @@ public class ccAnalysis
       double[] fNew = new double[f0.length];
       double costNew = 0;
       double stepHnorm = 0;
-      
+
       while (actualReduction <= 0 && nfev < maxNfev)
       {
-        TrustRegion trustRegion = solveLsqTrustRegion(n, m, uf, s, V, Delta, alpha);
-       double[] stepH = trustRegion.getStep(); 
-       alpha = trustRegion.getAlpha();
-       int nIterations = trustRegion.getIteration();
-        double predictedReduction = - (evaluateQuadratic(J, g, stepH));        
+        TrustRegion trustRegion = solveLsqTrustRegion(n, m, uf, s, V, Delta,
+                alpha);
+        double[] stepH = trustRegion.getStep();
+        alpha = trustRegion.getAlpha();
+        int nIterations = trustRegion.getIteration();
+        double predictedReduction = -(evaluateQuadratic(J, g, stepH));
 
         xNew = MiscMath.elementwiseAdd(hA, stepH);
-       fNew = originalToEquasionSystem(xNew, repMatrix, scoresOld, index);
-       nfev++;
-       
-       stepHnorm = MiscMath.norm(stepH);
+        fNew = originalToEquasionSystem(xNew, repMatrix, scoresOld, index);
+        nfev++;
 
-       if (MiscMath.countNaN(fNew) > 0)
-       {
-         Delta = 0.25 * stepHnorm;
-         continue;
-       }
+        stepHnorm = MiscMath.norm(stepH);
 
-       // usual trust-region step quality estimation
-       costNew = 0.5 * MiscMath.dot(fNew, fNew); 
-       actualReduction = cost - costNew;
+        if (MiscMath.countNaN(fNew) > 0)
+        {
+          Delta = 0.25 * stepHnorm;
+          continue;
+        }
 
-       double[] updatedTrustRegion = updateTrustRegionRadius(Delta, actualReduction, predictedReduction, stepHnorm, stepHnorm > (0.95 * Delta));
-       double DeltaNew = updatedTrustRegion[0];
-       double ratio = updatedTrustRegion[1];
+        // usual trust-region step quality estimation
+        costNew = 0.5 * MiscMath.dot(fNew, fNew);
+        actualReduction = cost - costNew;
 
-        // default ftol and xtol = 1e-8
-        boolean ftolSatisfied = actualReduction < (1e-8 * cost) && ratio > 0.25;
-        boolean xtolSatisfied = stepHnorm < (1e-8 * (1e-8 + MiscMath.norm(hA)));
-       terminationStatus = ftolSatisfied || xtolSatisfied;
-       if (terminationStatus)
-       {
-         break;
-       }
+        double[] updatedTrustRegion = updateTrustRegionRadius(Delta,
+                actualReduction, predictedReduction, stepHnorm,
+                stepHnorm > (0.95 * Delta));
+        double DeltaNew = updatedTrustRegion[0];
+        double ratio = updatedTrustRegion[1];
 
-       alpha *= Delta / DeltaNew;
-       Delta = DeltaNew;
+        // default ftol and xtol = 1e-8
+        boolean ftolSatisfied = actualReduction < (1e-8 * cost)
+                && ratio > 0.25;
+        boolean xtolSatisfied = stepHnorm < (1e-8
+                * (1e-8 + MiscMath.norm(hA)));
+        terminationStatus = ftolSatisfied || xtolSatisfied;
+        if (terminationStatus)
+        {
+          break;
+        }
+
+        alpha *= Delta / DeltaNew;
+        Delta = DeltaNew;
 
       }
       if (actualReduction > 0)
       {
-       hA = xNew;
-       f0 = fNew;
-       cost = costNew;
+        hA = xNew;
+        f0 = fNew;
+        cost = costNew;
 
-       J = approximateDerivative(repMatrix, scoresOld, index);
+        J = approximateDerivative(repMatrix, scoresOld, index);
 
         g = J.transpose().sumProduct(f0);
-      } else {
+      }
+      else
+      {
         stepHnorm = 0;
-       actualReduction = 0;
+        actualReduction = 0;
       }
       iteration++;
     }
@@ -828,16 +942,20 @@ public class ccAnalysis
   }
 
   /**
-  * performs the least squares optimisation
-  * adapted from https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.least_squares.html#scipy.optimize.least_squares
-  *
-  * @param repMatrix ~ Matrix containing representative vectors
-  * @param scoresOld ~ Matrix containing initial observations
-  * @param index ~ current row index
-  *
-  * @return
-  */
-  private double[] leastSquaresOptimisation(MatrixI repMatrix, MatrixI scoresOld, int index)
+   * performs the least squares optimisation adapted from
+   * https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.least_squares.html#scipy.optimize.least_squares
+   *
+   * @param repMatrix
+   *          ~ Matrix containing representative vectors
+   * @param scoresOld
+   *          ~ Matrix containing initial observations
+   * @param index
+   *          ~ current row index
+   *
+   * @return
+   */
+  private double[] leastSquaresOptimisation(MatrixI repMatrix,
+          MatrixI scoresOld, int index)
   {
     MatrixI J = approximateDerivative(repMatrix, scoresOld, index);
     double[] result = trf(repMatrix, scoresOld, index, J);
index 55510bd..1bdaad6 100644 (file)
@@ -57,5 +57,5 @@ public abstract class DistanceScoreModel implements ScoreModelI
 
     return similarities;
   }
-  
+
 }
index df45a72..bcc0855 100644 (file)
@@ -235,7 +235,7 @@ public class FeatureDistanceModel extends DistanceScoreModel
   public boolean isProtein()
   {
     return true;
-  } 
+  }
 
   @Override
   public String toString()
index f591c8e..490ec00 100644 (file)
@@ -40,7 +40,7 @@ public class ScoreModels
   private final ScoreMatrix PAM250;
 
   private final ScoreMatrix DNA;
-  
+
   private final ScoreMatrix SECONDARYSTRUCTURE;
 
   private static ScoreModels instance;
@@ -70,7 +70,7 @@ public class ScoreModels
    * <li>PAM250</li>
    * <li>PID</li>
    * <li>DNA</li>
-   * <li>Sequence Feature Similarity</li>   * 
+   * <li>Sequence Feature Similarity</li> *
    * <li>Secondary Structure Similarity</li>
    * </ul>
    */
@@ -85,8 +85,9 @@ public class ScoreModels
     DNA = loadScoreMatrix("scoreModel/dna.scm");
     registerScoreModel(new PIDModel());
     registerScoreModel(new FeatureDistanceModel());
-    SECONDARYSTRUCTURE = loadScoreMatrix("scoreModel/secondarystructure.scm");
-    registerScoreModel(new SecondaryStructureDistanceModel());   
+    SECONDARYSTRUCTURE = loadScoreMatrix(
+            "scoreModel/secondarystructure.scm");
+    registerScoreModel(new SecondaryStructureDistanceModel());
 
   }
 
@@ -146,7 +147,8 @@ public class ScoreModels
 
   public void registerScoreModel(ScoreModelI sm)
   {
-    if(sm.getName().equals("SECONDARYSTRUCTURE")) {
+    if (sm.getName().equals("SECONDARYSTRUCTURE"))
+    {
       return;
     }
     ScoreModelI sm2 = models.get(sm.getName());
@@ -187,7 +189,7 @@ public class ScoreModels
   {
     return PAM250;
   }
-  
+
   public ScoreMatrix getSecondaryStructureMatrix()
   {
     return SECONDARYSTRUCTURE;
index d7859f1..36d55de 100644 (file)
@@ -47,20 +47,19 @@ public class SecondaryStructureDistanceModel extends DistanceScoreModel
   private static final String NAME = "Secondary Structure Similarity";
 
   private ScoreMatrix ssRateMatrix;
-  
-  private String description;    
-  
+
+  private String description;
+
   FeatureRenderer fr;
-  
-  
+
   /**
    * Constructor
    */
   public SecondaryStructureDistanceModel()
   {
-    
+
   }
-  
+
   @Override
   public ScoreModelI getInstance(AlignmentViewPanel view)
   {
@@ -87,27 +86,31 @@ public class SecondaryStructureDistanceModel extends DistanceScoreModel
     fr = view.cloneFeatureRenderer();
     return true;
   }
-  
+
   /**
-   * Calculates distance score [i][j] between each pair of protein sequences 
-   * based on their secondary structure annotations (H, E, C). 
-   * The final score is normalised by the number of 
-   * alignment columns processed, providing an average similarity score.
+   * Calculates distance score [i][j] between each pair of protein sequences
+   * based on their secondary structure annotations (H, E, C). The final score
+   * is normalised by the number of alignment columns processed, providing an
+   * average similarity score.
    * <p>
-   * The parameters argument can include settings for handling gap-residue aligned 
-   * positions and may determine if the score calculation is based on the longer or shorter 
-   * sequence in each pair. This can be important for handling partial alignments or 
-   * sequences of significantly different lengths.
+   * The parameters argument can include settings for handling gap-residue
+   * aligned positions and may determine if the score calculation is based on
+   * the longer or shorter sequence in each pair. This can be important for
+   * handling partial alignments or sequences of significantly different
+   * lengths.
    * 
-   * @param seqData  The aligned sequence data including secondary structure annotations.
-   * @param params   Additional parameters for customising the scoring process, such as gap 
-   *                 handling and sequence length consideration.
+   * @param seqData
+   *          The aligned sequence data including secondary structure
+   *          annotations.
+   * @param params
+   *          Additional parameters for customising the scoring process, such as
+   *          gap handling and sequence length consideration.
    */
   @Override
   public MatrixI findDistances(AlignmentView seqData,
           SimilarityParamsI params)
-  {   
-    
+  {
+
     SeqCigar[] seqs = seqData.getSequences();
     int noseqs = seqs.length; //no of sequences
     int cpwidth = 0; 
@@ -125,7 +128,6 @@ public class SecondaryStructureDistanceModel extends DistanceScoreModel
     
     AlignmentAnnotation[] alignAnnotList = fr.getViewport().getAlignment()
             .getAlignmentAnnotation();   
-    
 
     /*
      * Add secondary structure annotations that are added to the annotation track
@@ -134,6 +136,7 @@ public class SecondaryStructureDistanceModel extends DistanceScoreModel
     Map<SequenceI, ArrayList<AlignmentAnnotation>> ssAlignmentAnnotationForSequences 
       = AlignmentUtils.getSequenceAssociatedAlignmentAnnotations(alignAnnotList, ssSource); 
 
+
     /*
      * scan each column, compute and add to each similarity[i, j]
      * the number of secondary structure annotation that seqi 
@@ -141,15 +144,16 @@ public class SecondaryStructureDistanceModel extends DistanceScoreModel
      */
     for (int vc = 0; vc < viscont.length; vc += 2)
     {
-      //Iterates for each column position
-      for (int cpos = viscont[vc]; cpos <= viscont[vc + 1]; cpos++) 
+      // Iterates for each column position
+      for (int cpos = viscont[vc]; cpos <= viscont[vc + 1]; cpos++)
       {
-        cpwidth++; //used to normalise the similarity score 
+        cpwidth++; // used to normalise the similarity score
 
         /*
          * get set of sequences without gap in the current column
          */
-        Set<SeqCigar> seqsWithoutGapAtCol = findSeqsWithoutGapAtColumn(seqs, cpos);
+        Set<SeqCigar> seqsWithoutGapAtCol = findSeqsWithoutGapAtColumn(seqs,
+                cpos);
 
         /*
          * calculate similarity score for each secondary structure annotation on i'th and j'th
@@ -158,7 +162,7 @@ public class SecondaryStructureDistanceModel extends DistanceScoreModel
          */
         for (int i = 0; i < (noseqs - 1); i++)
         {
-          //Iterates for each sequences
+          // Iterates for each sequences
           for (int j = i + 1; j < noseqs; j++)
           {
                          
@@ -167,15 +171,10 @@ public class SecondaryStructureDistanceModel extends DistanceScoreModel
             boolean undefinedSS2 = ssAlignmentAnnotationForSequences.get(seqs[j].getRefSeq()) == null;
 
             // Set similarity to max score if both SS are not defined
-            if (undefinedSS1 && undefinedSS2) {
-                similarities[i][j] += ssRateMatrix.getMaximumScore();
-                continue;
-            } 
-            
-            // Set similarity to minimum score if either one SS is not defined
-            else if(undefinedSS1 || undefinedSS2) {
-                similarities[i][j] += ssRateMatrix.getMinimumScore();
-                continue;
+            if (undefinedSS1 && undefinedSS2)
+            {
+              similarities[i][j] += ssRateMatrix.getMaximumScore();
+              continue;
             }
             
             //check if the sequence contains gap in the current column
@@ -183,23 +182,27 @@ public class SecondaryStructureDistanceModel extends DistanceScoreModel
             boolean gap2 = !seqsWithoutGapAtCol.contains(seqs[j]);            
             
             //Variable to store secondary structure at the current column
+
             char ss1 = '*';
             char ss2 = '*';
-            
-            //secondary structure is fetched only if the current column is not 
-            //gap for the sequence
-            if(!gap1 && !undefinedSS1) {  
-              //fetch the position in sequence for the column and finds the
-              //corresponding secondary structure annotation
-              //TO DO - consider based on priority and displayed
+
+            // secondary structure is fetched only if the current column is not
+            // gap for the sequence
+            if (!gap1 && !undefinedSS1)
+            {
+              // fetch the position in sequence for the column and finds the
+              // corresponding secondary structure annotation
+              // TO DO - consider based on priority and displayed
               int seqPosition = seqs[i].findPosition(cpos);
               AlignmentAnnotation aa = ssAlignmentAnnotationForSequences.get(seqs[i].getRefSeq()).get(0);
               if(aa!=null)
               ss1 = 
                   AlignmentUtils.findSSAnnotationForGivenSeqposition(aa, seqPosition);              
+
             }
-            
-            if(!gap2 && !undefinedSS2) {              
+
+            if (!gap2 && !undefinedSS2)
+            {
               int seqPosition = seqs[j].findPosition(cpos);
               AlignmentAnnotation aa = ssAlignmentAnnotationForSequences.get(seqs[j].getRefSeq()).get(0);
               if(aa!=null)
@@ -207,10 +210,12 @@ public class SecondaryStructureDistanceModel extends DistanceScoreModel
                   AlignmentUtils.findSSAnnotationForGivenSeqposition(aa, seqPosition);               
             }           
 
+
             if ((!gap1 && !gap2) || params.includeGaps())
             {
               // Calculate similarity score based on the substitution matrix
-              double similarityScore = ssRateMatrix.getPairwiseScore(ss1, ss2);
+              double similarityScore = ssRateMatrix.getPairwiseScore(ss1,
+                      ss2);
               similarities[i][j] += similarityScore;
             }
           }
@@ -224,30 +229,30 @@ public class SecondaryStructureDistanceModel extends DistanceScoreModel
      * and fill in the bottom half of the matrix
      */
     // TODO JAL-2424 cpwidth may be out by 1 - affects scores but not tree shape
-    
+
     for (int i = 0; i < noseqs; i++)
     {
       for (int j = i + 1; j < noseqs; j++)
-      {        
+      {
         similarities[i][j] /= cpwidth;
         similarities[j][i] = similarities[i][j];
       }
     }
     return ssRateMatrix.similarityToDistance(new Matrix(similarities));
-    
+
   }
 
   /**
-   * Builds and returns a set containing sequences (SeqCigar) which do not
-   * have a gap at the given column position.
+   * Builds and returns a set containing sequences (SeqCigar) which do not have
+   * a gap at the given column position.
    * 
    * @param seqs
    * @param columnPosition
    *          (0..)
    * @return
    */
-  private Set<SeqCigar> findSeqsWithoutGapAtColumn(
-          SeqCigar[] seqs, int columnPosition)
+  private Set<SeqCigar> findSeqsWithoutGapAtColumn(SeqCigar[] seqs,
+          int columnPosition)
   {
     Set<SeqCigar> seqsWithoutGapAtCol = new HashSet<>();
     for (SeqCigar seq : seqs)
@@ -257,14 +262,13 @@ public class SecondaryStructureDistanceModel extends DistanceScoreModel
       {
         /*
          * position is not a gap
-         */        
+         */
         seqsWithoutGapAtCol.add(seq);
       }
     }
     return seqsWithoutGapAtCol;
   }
 
-    
   @Override
   public String getName()
   {
@@ -280,7 +284,7 @@ public class SecondaryStructureDistanceModel extends DistanceScoreModel
   @Override
   public boolean isDNA()
   {
-    return false; 
+    return false;
   }
 
   @Override
@@ -288,7 +292,7 @@ public class SecondaryStructureDistanceModel extends DistanceScoreModel
   {
     return false;
   }
-  
+
   @Override
   public boolean isSecondaryStructure()
   {
index 2a25fa8..40a4636 100644 (file)
@@ -103,7 +103,7 @@ public class SimilarityParams implements SimilarityParamsI
   private boolean includeGaps;
 
   private boolean denominateByShortestLength;
-  
+
   private String secondaryStructureSource;
 
   /**
index 25e4873..098024c 100644 (file)
@@ -121,7 +121,9 @@ public interface AlignViewControllerI
 
   /**
    * Justify alignment or currently selected region left or right
-   * @param left - true - means justify left
+   * 
+   * @param left
+   *          - true - means justify left
    * @return
    */
   boolean justify_Region(boolean left);
index 2cdb251..64e06ee 100644 (file)
@@ -132,7 +132,6 @@ public interface AlignViewportI extends ViewStyleI
   
   List<AlignmentAnnotation> getAlignmentSecondaryStructureConsensusAnnotation();
 
-
   /**
    * get the container for alignment gap annotation
    * 
@@ -180,7 +179,6 @@ public interface AlignViewportI extends ViewStyleI
   void setSequenceConsensusHash(ProfilesI hconsensus);
   
   void setSequenceSSConsensusHash(Map<String, ProfilesI> hSSConsesnusProfileMap);
-  
 
   /**
    * Set the cDNA complement consensus for the viewport
@@ -571,6 +569,7 @@ public interface AlignViewportI extends ViewStyleI
    * @return
    */
   Iterator<int[]> getViewAsVisibleContigs(boolean selectedRegionOnly);
+
   /**
    * notify all concerned that the alignment data has changed and derived data
    * needs to be recalculated
@@ -578,7 +577,8 @@ public interface AlignViewportI extends ViewStyleI
   public void notifyAlignmentChanged();
 
   /**
-   * retrieve a matrix associated with the view's alignment's annotation 
+   * retrieve a matrix associated with the view's alignment's annotation
+   * 
    * @param alignmentAnnotation
    * @return contact matrix or NULL
    */
index a243c0c..ce415c2 100644 (file)
@@ -62,21 +62,18 @@ public interface ScoreModelI
 
   // TODO getName, isDNA, isProtein can be static methods in Java 8
 
-  
   default public boolean isSecondaryStructure()
   {
     return false;
   }
 
   /**
-   * Answers false by default 
-   * Answers true if the data has secondary structure (so should be
-   * shown in menus in that context)
+   * Answers false by default Answers true if the data has secondary structure
+   * (so should be shown in menus in that context)
    * 
    * @return
    */
-  
-  
+
   /**
    * Returns a distance score for the given sequence regions, that is, a matrix
    * whose value [i][j] is the distance of sequence i from sequence j by some
index 43f2866..2558515 100644 (file)
@@ -60,7 +60,7 @@ public interface SimilarityParamsI
    * @return
    */
   boolean denominateByShortestLength();
-  
+
   String getSecondaryStructureSource();
 
   void setSecondaryStructureSource(String secondaryStructureSource);
index 55a5364..b77ee85 100644 (file)
@@ -3432,8 +3432,8 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     Menu autoAnnMenu = new Menu(
             MessageManager.getString("label.autocalculated_annotation"));
 
-    Menu selectSS = new Menu(
-            MessageManager.getString("label.select_secondary_structure_source"));
+    Menu selectSS = new Menu(MessageManager
+            .getString("label.select_secondary_structure_source"));
     showGroupConsensus.addItemListener(this);
     showGroupConservation.addItemListener(this);
     showConsensusHistogram.addItemListener(this);
index a45fc42..6a82e7d 100755 (executable)
@@ -68,6 +68,7 @@ import jalview.urls.IdOrgSettings;
 import jalview.util.ChannelProperties;
 import jalview.util.ColorUtils;
 import jalview.util.HttpUtils;
+import jalview.util.LaunchUtils;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
 import jalview.ws.sifts.SiftsSettings;
@@ -1273,6 +1274,14 @@ public class Cache
       sb.append(" (");
       sb.append(lafClass);
       sb.append(")\n");
+      // pid() only available in Java 9+
+      if (LaunchUtils.getJavaVersion() > 8)
+      {
+        sb.append("Java Virtual Machine PID: ");
+        sb.append(ProcessHandle.current().pid());
+        sb.append("\n");
+      }
+
     }
     appendIfNotNull(sb, "Installer version: ",
             System.getProperty("sys.install4jVersion"), "\n", null);
@@ -1309,6 +1318,8 @@ public class Cache
               System.getProperty("launcher.distdir"), "\n", null);
       appendIfNotNull(sb, "Launcher appbase: ",
               System.getProperty("launcher.appbase"), "\n", null);
+      appendIfNotNull(sb, "Launcher script: ",
+              System.getProperty("launcher.script"), "\n", null);
     }
     return sb.toString();
   }
@@ -1489,11 +1500,10 @@ public class Cache
                 if (customProxySet &&
                 // we have a username but no password for the scheme being
                 // requested
-                        (protocol.equalsIgnoreCase("http")
-                                && (httpUser != null
-                                        && httpUser.length() > 0
-                                        && (httpPassword == null
-                                                || httpPassword.length == 0)))
+                (protocol.equalsIgnoreCase("http")
+                        && (httpUser != null && httpUser.length() > 0
+                                && (httpPassword == null
+                                        || httpPassword.length == 0)))
                         || (protocol.equalsIgnoreCase("https")
                                 && (httpsUser != null
                                         && httpsUser.length() > 0
diff --git a/src/jalview/bin/GetdownLauncherUpdate.java b/src/jalview/bin/GetdownLauncherUpdate.java
new file mode 100644 (file)
index 0000000..ec1f322
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.bin;
+
+import java.io.File;
+
+import com.threerings.getdown.data.EnvConfig;
+import com.threerings.getdown.util.LaunchUtil;
+
+public class GetdownLauncherUpdate
+{
+  public static void main(String[] args)
+  {
+    EnvConfig.setVarsFromProperties();
+
+    String appdir = args.length > 0 ? args[0] : null;
+    if (appdir == null || appdir.length() == 0)
+    {
+      appdir = System.getProperty("launcher.appdir", null);
+    }
+    if (appdir == null)
+    {
+      appdir = EnvConfig.getUserAppdir();
+    }
+    if (appdir == null)
+    {
+      System.err.println("Not running upgradeGetdown");
+      return;
+    }
+    boolean debug = false;
+    for (int i = 0; i < args.length; i++)
+    {
+      if ("--debug".equals(args[i]))
+      {
+        debug = true;
+        break;
+      }
+    }
+    if (debug)
+    {
+      System.err.println("Running upgradeGetdown");
+    }
+    File appdirFile = new File(appdir);
+    if (!appdirFile.exists())
+    {
+      System.err.println("Directory '" + appdirFile.getAbsolutePath()
+              + "' doesn't exist, cannot update getdown-launcher.jar.");
+      if (Boolean.valueOf(System.getProperty("launcher.update")))
+      {
+        System.exit(1);
+      }
+    }
+    LaunchUtil.upgradeGetdown(new File(appdir, "getdown-launcher-old.jar"),
+            new File(appdir, "getdown-launcher.jar"),
+            new File(appdir, "getdown-launcher-new.jar"));
+  }
+}
index 0e3b30a..8bddae3 100755 (executable)
@@ -64,7 +64,6 @@ import javax.swing.UnsupportedLookAndFeelException;
 import com.formdev.flatlaf.FlatLightLaf;
 import com.formdev.flatlaf.themes.FlatMacLightLaf;
 import com.formdev.flatlaf.util.SystemInfo;
-import com.threerings.getdown.util.LaunchUtil;
 
 //import edu.stanford.ejalbert.launching.IBrowserLaunching;
 import groovy.lang.Binding;
@@ -383,17 +382,13 @@ public class Jalview implements JalviewObjectI
     String appdirString = System.getProperty("launcher.appdir");
     if (appdirString != null && appdirString.length() > 0)
     {
-      final File appdir = new File(appdirString);
       new Thread()
       {
 
         @Override
         public void run()
         {
-          LaunchUtil.upgradeGetdown(
-                  new File(appdir, "getdown-launcher-old.jar"),
-                  new File(appdir, "getdown-launcher.jar"),
-                  new File(appdir, "getdown-launcher-new.jar"));
+          GetdownLauncherUpdate.main(new String[] { appdirString });
         }
       }.start();
     }
index 7e1296e..2e2dd49 100644 (file)
@@ -2251,8 +2251,8 @@ public class JalviewLite extends Applet
         PDBEntry[] pdb = new PDBEntry[pdbs.size()];
         String[][] chains = new String[pdbs.size()][];
         String[] protocols = new String[pdbs.size()];
-        for (int pdbsi = 0,
-                pdbsiSize = pdbs.size(); pdbsi < pdbsiSize; pdbsi++)
+        for (int pdbsi = 0, pdbsiSize = pdbs
+                .size(); pdbsi < pdbsiSize; pdbsi++)
         {
           Object[] o = (Object[]) pdbs.elementAt(pdbsi);
           pdb[pdbsi] = (PDBEntry) o[0];
index ec53219..579b682 100644 (file)
@@ -210,6 +210,8 @@ public class Launcher
       headless = assumeheadless;
     }
 
+    ErrorLog.setQuiet(quiet);
+
     final String appName = ChannelProperties.getProperty("app_name");
 
     // if we're using jalview.bin.Launcher we always assume a console is in use
@@ -327,8 +329,8 @@ public class Launcher
     String scalePropertyArg = HiDPISetting.getScalePropertyArg();
     if (scalePropertyArg != null)
     {
-      LaunchUtils.syserr(debug, quiet, "Running " + startClass
-              + " with scale setting " + scalePropertyArg);
+      ErrorLog.errPrintln("Running " + startClass + " with scale setting "
+              + scalePropertyArg);
       addJvmArgs.add(scalePropertyArg);
     }
 
@@ -336,7 +338,7 @@ public class Launcher
             addJvmArgs, null, null, null, startClass, null, null, arguments,
             launcherprint, launcherwait, launcherstop, debug, quiet);
 
-    LaunchUtils.syserr(debug, quiet, "JVM exited with value " + exitValue);
+    ErrorLog.errPrintln("JVM exited with value " + exitValue);
   }
 
 }
index 2559662..c1eb655 100644 (file)
@@ -58,8 +58,8 @@ public class JustifyLeftOrRightCommand extends EditCommand
       {
         continue;
       }
-      char[] range = seq.getSequence(from, to+1);
-      if (range==null || range.length==0)
+      char[] range = seq.getSequence(from, to + 1);
+      if (range == null || range.length == 0)
       {
         continue;
       }
@@ -78,7 +78,7 @@ public class JustifyLeftOrRightCommand extends EditCommand
         alseq[gapstart + gp] = gc;
       }
 
-      for (int sqp = 0,insp=0; sqp<alseq.length; sqp++)
+      for (int sqp = 0, insp = 0; sqp < alseq.length; sqp++)
       {
         if (sqp < range.length && !Comparison.isGap(range[sqp]))
         {
index bccacfa..3ab90e6 100644 (file)
@@ -500,7 +500,7 @@ public class AlignViewController implements AlignViewControllerI
     // Technically we should return false, since view has not changed
     return false;
   }
-  
+
   @Override
   public boolean justify_Region(boolean left)
   {
index 45c0d18..b2c6e4a 100644 (file)
@@ -1,3 +1,23 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
 package jalview.datamodel;
 
 import java.util.Collection;
index 342abf8..ffd49ea 100644 (file)
@@ -1,3 +1,23 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
 package jalview.datamodel;
 
 import java.util.Collection;
index faeb7c0..bf047d6 100644 (file)
@@ -1,3 +1,23 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
 package jalview.datamodel;
 
 /**
index c3906f9..b6b13c9 100755 (executable)
@@ -649,9 +649,9 @@ public class PDBEntry
 
   public void setProviderCategory(String providerCategory)
   {
-    setProperty(PROVIDERCATEGORY, providerCategory);    
+    setProperty(PROVIDERCATEGORY, providerCategory);
   }
-  
+
   public String getProviderCategory()
   {
     return (String) getProperty(PROVIDERCATEGORY);
@@ -661,5 +661,5 @@ public class PDBEntry
   {
     return _hasProperty(PROVIDERCATEGORY);
   }
-  
+
 }
index bc87204..ae538f9 100644 (file)
@@ -32,7 +32,7 @@ public class Profile implements ProfileI
    * an object holding counts of symbols in the profile
    */
   private ResidueCount counts;
-  
+
   private SecondaryStructureCount ssCounts;
   
   private int seqWithSSCount = -1;
@@ -51,15 +51,15 @@ public class Profile implements ProfileI
    * the highest count for any residue in the profile
    */
   private int maxCount;
+
   private int maxSSCount;
-  
 
   /*
    * the residue (e.g. K) or residues (e.g. KQW) with the
    * highest count in the profile
    */
   private String modalResidue;
-  
+
   private String modalSS;
 
   /**
@@ -100,7 +100,7 @@ public class Profile implements ProfileI
   {
     this.counts = residueCounts;
   }
-  
+
   @Override
   public void setSSCounts(SecondaryStructureCount secondaryStructureCount)
   {
@@ -128,7 +128,7 @@ public class Profile implements ProfileI
     }
     return pid;
   }
-  
+
   @Override
   public float getSSPercentageIdentity(boolean ignoreGaps)
   {
@@ -156,7 +156,7 @@ public class Profile implements ProfileI
   {
     return counts;
   }
-  
+
   @Override
   public SecondaryStructureCount getSSCounts()
   {
@@ -189,7 +189,7 @@ public class Profile implements ProfileI
   {
     return maxCount;
   }
-  
+
   @Override
   public int getMaxSSCount()
   {
@@ -204,7 +204,7 @@ public class Profile implements ProfileI
   {
     return modalResidue;
   }
-  
+
   @Override
   public String getModalSS()
   {
index 9046f23..084cfde 100644 (file)
@@ -29,8 +29,9 @@ public interface ProfileI
    * @param residueCounts
    */
   public abstract void setCounts(ResidueCount residueCounts);
-  public abstract void setSSCounts(SecondaryStructureCount secondaryStructureCount);
-  
+
+  public abstract void setSSCounts(
+          SecondaryStructureCount secondaryStructureCount);
 
   /**
    * Returns the percentage identity of the profile, i.e. the highest proportion
@@ -41,7 +42,7 @@ public interface ProfileI
    * @return
    */
   public abstract float getPercentageIdentity(boolean ignoreGaps);
-  
+
   public abstract float getSSPercentageIdentity(boolean ignoreGaps);
 
   /**
@@ -74,6 +75,7 @@ public interface ProfileI
    * @return
    */
   public abstract int getMaxCount();
+
   public abstract int getMaxSSCount();
 
   /**
@@ -83,6 +85,7 @@ public interface ProfileI
    * @return
    */
   public abstract String getModalResidue();
+
   public abstract String getModalSS();
 
   /**
@@ -91,6 +94,7 @@ public interface ProfileI
    * @return
    */
   public abstract int getNonGapped();
+
   SecondaryStructureCount getSSCounts();
 
 }
\ No newline at end of file
index 52e9746..7d1b43c 100644 (file)
@@ -63,7 +63,6 @@ public class SecondaryStructureCount
    */
   private static final String SS_SYMBOLS = "HEC";
 
-
   static final int GAP_COUNT = 0;
 
   /*
@@ -116,7 +115,7 @@ public class SecondaryStructureCount
    */
   public SecondaryStructureCount()
   {
-    //isSS = true;
+    // isSS = true;
     int charsToCount = SS_SYMBOLS.length();
     counts = new short[charsToCount + 1];
   }
@@ -419,8 +418,7 @@ public class SecondaryStructureCount
       {
         if (intCounts[i] == count)
         {
-          modal.append(
-                  SS_SYMBOLS.charAt(i - 1));
+          modal.append(SS_SYMBOLS.charAt(i - 1));
         }
       }
     }
@@ -430,8 +428,7 @@ public class SecondaryStructureCount
       {
         if (counts[i] == count)
         {
-          modal.append(
-                  SS_SYMBOLS.charAt(i - 1));
+          modal.append(SS_SYMBOLS.charAt(i - 1));
         }
       }
     }
index 38104e5..5248a58 100755 (executable)
@@ -136,7 +136,6 @@ public class SequenceGroup implements AnnotatedCollectionI
    * consensus calculation property
    */
   private boolean showSequenceLogo = false;
-  
 
   /**
    * flag indicating if logo should be rendered normalised
@@ -154,7 +153,6 @@ public class SequenceGroup implements AnnotatedCollectionI
   private boolean hidecols = false;
 
   AlignmentAnnotation consensus = null;
-  
 
   List<AlignmentAnnotation> ssConsensus = null;
   
@@ -163,7 +161,7 @@ public class SequenceGroup implements AnnotatedCollectionI
   AlignmentAnnotation conservation = null;
 
   private boolean showConsensusHistogram;
-  
+
   private AnnotatedCollectionI context;
   
   public Map<String, ProfilesI> hSSConsensusProfileMap;
@@ -651,7 +649,6 @@ public class SequenceGroup implements AnnotatedCollectionI
           hSSConsensusProfileMap.put(ssSource, hSSConsensus);
         }
       }
-            
 
       if (ssConsensus != null)
       {
@@ -664,7 +661,6 @@ public class SequenceGroup implements AnnotatedCollectionI
         cs.setSSConsensusProfileMap(hSSConsensusProfileMap);
         upd = true;
       }
-      
 
       if ((conservation != null)
               || (cs != null && cs.conservationApplied()))
@@ -1429,7 +1425,7 @@ public class SequenceGroup implements AnnotatedCollectionI
     }
     this.showSequenceLogo = showSequenceLogo;
   }
-  
+
   /**
    * 
    * @param showConsHist
@@ -1447,6 +1443,7 @@ public class SequenceGroup implements AnnotatedCollectionI
     this.showConsensusHistogram = showConsHist;
   }
 
+
   /**
    * @return the showConsensusHistogram
    */
index 4e9553e..11d58d9 100644 (file)
@@ -1,3 +1,23 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
 package jalview.datamodel.annotations;
 
 import jalview.datamodel.Annotation;
index 3cc778a..9521305 100644 (file)
@@ -70,9 +70,9 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher
    * @see https://github.com/Ensembl/ensembl-rest/wiki/Change-log
    * @see http://rest.ensembl.org/info/rest?content-type=application/json
    */
-  private static final String LATEST_ENSEMBLGENOMES_REST_VERSION = "15.2";
+  private static final String LATEST_ENSEMBLGENOMES_REST_VERSION = "15.8";
 
-  private static final String LATEST_ENSEMBL_REST_VERSION = "15.2";
+  private static final String LATEST_ENSEMBL_REST_VERSION = "15.8";
 
   private static final String REST_CHANGE_LOG = "https://github.com/Ensembl/ensembl-rest/wiki/Change-log";
 
index 7a06638..a624a66 100644 (file)
@@ -5915,7 +5915,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     viewport.setShowSSConsensus(showSSConsensus.getState());
     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
-
   }
 
   /*
@@ -5969,6 +5968,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
   }
 
+
   /*
    * (non-Javadoc)
    * 
index 9430a8b..fa48bab 100644 (file)
@@ -282,12 +282,13 @@ public class AlignViewport extends AlignmentViewport
       }
       showConsensusHistogram = Cache.getDefault("SHOW_CONSENSUS_HISTOGRAM",
               true);
+
       showSequenceLogo = Cache.getDefault("SHOW_CONSENSUS_LOGO", true);
       normaliseSequenceLogo = Cache.getDefault("NORMALISE_CONSENSUS_LOGO",
               false);
       showGroupConsensus = Cache.getDefault("SHOW_GROUP_CONSENSUS", false);
       showConsensus = Cache.getDefault("SHOW_IDENTITY", true);
-      
+
       showSSConsensus = Cache.getDefault("SHOW_SS_CONSENSUS", false);
 
       showOccupancy = Cache.getDefault(Preferences.SHOW_OCCUPANCY, true);
index 4d0de1b..d37c9c0 100644 (file)
@@ -423,12 +423,13 @@ public class AppJmol extends StructureViewerBase
 
   public boolean isRepainting()
   {
-    if (renderPanel!=null && renderPanel.isVisible())
+    if (renderPanel != null && renderPanel.isVisible())
     {
       return renderPanel.repainting;
     }
     return false;
   }
+
   /**
    * Outputs the Jmol viewer image as an image file, after prompting the user to
    * choose a file and (for EPS) choice of Text or Lineart character rendering
@@ -441,10 +442,12 @@ public class AppJmol extends StructureViewerBase
   {
     while (!isRepainting())
     {
-      try {
+      try
+      {
         Thread.sleep(2);
       } catch (Exception q)
-      {}
+      {
+      }
     }
     try
     {
@@ -642,17 +645,18 @@ public class AppJmol extends StructureViewerBase
       }
       else
       {
-        repainting=true;
+        repainting = true;
         synchronized (jmb)
         {
           jmb.jmolViewer.renderScreenImage(g, currentSize.width,
                   currentSize.height);
-          
+
         }
-        repainting=false;
+        repainting = false;
       }
     }
-    volatile boolean repainting=false;
+
+    volatile boolean repainting = false;
   }
 
   @Override
index b831f43..14c2e1e 100644 (file)
@@ -28,6 +28,7 @@ import jalview.api.analysis.SimilarityParamsI;
 import jalview.bin.Cache;
 import jalview.datamodel.SequenceGroup;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 import jalview.viewmodel.AlignmentViewport;
 import java.awt.BorderLayout;
 import java.awt.Color;
@@ -261,12 +262,17 @@ public class CalculationChooser extends JPanel
     pasimapBorderless.setOpaque(false); // false -> stops every pixel inside
                                         // border from being painted
     pasimapBorderless.add(pasimap, FlowLayout.LEFT); // add pasimap button to
-                                                     // the JPanel
-    calcChoicePanel.add(pasimapBorderless, FlowLayout.LEFT); // add button with
-                                                             // border and
-                                                             // everything to
-                                                             // the overall
-                                                             // ChoicePanel
+    // the JPanel
+    if (!Platform.isJS())
+    {
+      // FIXME JAL-4443
+      calcChoicePanel.add(pasimapBorderless, FlowLayout.LEFT); // add button
+                                                               // with
+      // border and
+      // everything to
+      // the overall
+      // ChoicePanel
+    }
 
     treePanel.add(neighbourJoining);
     treePanel.add(averageDistance);
@@ -276,7 +282,11 @@ public class CalculationChooser extends JPanel
 
     ButtonGroup calcTypes = new ButtonGroup();
     calcTypes.add(pca);
-    calcTypes.add(pasimap);
+    if (!Platform.isJS())
+    {
+      // FIXME JAL-4443
+      calcTypes.add(pasimap);
+    }
     calcTypes.add(neighbourJoining);
     calcTypes.add(averageDistance);
     calcTypes.add(pairwise);
@@ -639,8 +649,10 @@ public class CalculationChooser extends JPanel
    * 
    * @param nucleotide
    * @param forPca
-   * @param ssPresent - include secondary structure similarity model
-   * @param forPasimap - limit to ScoreMatrix based models - allows use of AlignSeq
+   * @param ssPresent
+   *          - include secondary structure similarity model
+   * @param forPasimap
+   *          - limit to ScoreMatrix based models - allows use of AlignSeq
    * @return
    */
   protected static List<ScoreModelI> getApplicableScoreModels(
@@ -652,7 +664,7 @@ public class CalculationChooser extends JPanel
     ScoreModels scoreModels = ScoreModels.getInstance();
     for (ScoreModelI sm : scoreModels.getModels())
     {
-      if ((!forPasimap || sm instanceof ScoreMatrix)  
+      if ((!forPasimap || sm instanceof ScoreMatrix)
               && (!nucleotide && sm.isProtein() || nucleotide && sm.isDNA()
                       || sm.isSecondaryStructure() && ssPresent))
 
index 299af6b..10aa229 100644 (file)
@@ -94,13 +94,14 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
 
   /**
    * set font size of the textarea
+   * 
    * @param size
    */
   public void setFont(Font font)
   {
     textarea.setFont(font);
   }
-  
+
   /**
    * DOCUMENT ME!
    */
@@ -217,11 +218,12 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
     {
     }
   }
+
   /**
-  * show menu for changing the font
-  * @param e
-  */
+   * show menu for changing the font
+   * 
+   * @param e
+   */
   @Override
   public void fontSizeMenu_actionPerformed(ActionEvent e)
   {
index c144268..10067cf 100644 (file)
@@ -1214,185 +1214,261 @@ public class Desktop extends jalview.jbgui.GDesktop
   }
 
   /**
-   * DOCUMENT ME!
-   * 
-   * @param evt
-   *          DOCUMENT ME!
+   * load all files dropped (2.11.4.0 behaviour)
+   * @return false if any file resulted in an error
    */
-  @Override
-  public void drop(DropTargetDropEvent evt)
+  public boolean loadDroppedFiles(List<Object> files,
+          List<DataSourceType> protocols, List<Object> failed,
+          List<Exception> failed_exceptions)
   {
-    boolean success = true;
-    // JAL-1552 - acceptDrop required before getTransferable call for
-    // Java's Transferable for native dnd
-    evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
-    Transferable t = evt.getTransferable();
-    List<Object> files = new ArrayList<>();
-    List<DataSourceType> protocols = new ArrayList<>();
-
-    try
+    for (int i = 0; i < files.size(); i++)
     {
-      Desktop.transferFromDropTarget(files, protocols, evt, t);
-    } catch (Exception e)
+      Object file = files.get(i);
+      try
+      {
+        // BH 2018 File or String
+        String fileName = file.toString();
+        DataSourceType protocol = (protocols == null) ? DataSourceType.FILE
+                : protocols.get(i);
+        FileFormatI format = null;
+
+        if (fileName.endsWith(".jar"))
+        {
+          format = FileFormat.Jalview;
+        }
+        else
+        {
+          format = new IdentifyFile().identify(file, protocol);
+        }
+        if (file instanceof File)
+        {
+          Platform.cacheFileData((File) file);
+        }
+        new FileLoader().LoadFile(null, file, protocol, format);
+      } catch (Exception x)
+      {
+        failed.add(file);
+        failed_exceptions.add(x);
+        jalview.bin.Console.warn(
+                "Unexpected Exception when handling " + file.toString(), x);
+      }
+    }
+    return failed.size()==0; // at least one was loaded.
+  }
+
+  /**
+   * analyse dropped files and process them according to type: load alignments,
+   * add annotation, trees or features to alignments with the same basename
+   * 
+   * @return false if any exception was raised
+   */
+  public boolean processAndLoadDroppedFiles(List<Object> files,
+          List<DataSourceType> protocols, List<Object> failed,
+          List<Exception> failed_exceptions)
+  {
+    // map list to list of strings
+    List<String> filenames = new ArrayList<>();
+    for (int i = 0; i < files.size(); i++)
     {
-      e.printStackTrace();
-      success = false;
+      filenames.add(files.get(i).toString());
     }
 
-    if (files != null)
+    // processFilenames will take likely associated files OUT of the list of
+    // filenames and return them in a BaseInfo object in the Map
+    Map<String, BaseInfo> baseInfoMap = ArgParserUtils
+            .processFilenames(filenames, false, files);
+    // so we can catch exceptions for this file
+    Object file = null;
+    try
     {
-      try
+
+      for (int i = 0; i < files.size(); i++)
       {
-        // map list to list of strings
-        List<String> filenames = new ArrayList<>();
-        for (int i = 0; i < files.size(); i++)
+        // BH 2018 File or String
+        file = files.get(i);
+        String fileName = file.toString();
+        DataSourceType protocol = (protocols == null) ? DataSourceType.FILE
+                : protocols.get(i);
+        FileFormatI format = null;
+
+        if (fileName.toLowerCase(Locale.ROOT).endsWith(".jar"))
         {
-          filenames.add(files.get(i).toString());
+          format = FileFormat.Jalview;
         }
-
-        // processFilenames will take likely associated files OUT of the list of
-        // filenames and return them in a BaseInfo object in the Map
-        Map<String, BaseInfo> baseInfoMap = ArgParserUtils
-                .processFilenames(filenames, false, files);
-        for (int i = 0; i < files.size(); i++)
+        else
         {
-          // BH 2018 File or String
-          Object file = files.get(i);
-          String fileName = file.toString();
-          DataSourceType protocol = (protocols == null)
-                  ? DataSourceType.FILE
-                  : protocols.get(i);
-          FileFormatI format = null;
-
-          if (fileName.toLowerCase(Locale.ROOT).endsWith(".jar"))
-          {
-            format = FileFormat.Jalview;
-          }
-          else
+          format = new IdentifyFile().identify(file, protocol);
+        }
+        // If features/annotations/tree file that hasn't been put into the
+        // baseInfoMap, look through titles of opened AlignFrames to add.
+        String ext = FileUtils.getExtension(fileName);
+        boolean isFeatures = ArgParserUtils.featuresExtensions
+                .contains(ext);
+        boolean isAnnotations = ArgParserUtils.annotationsExtensions
+                .contains(ext);
+        boolean isTree = ArgParserUtils.treeExtensions.contains(ext);
+        if (isFeatures || isAnnotations || isTree)
+        {
+          String base = FileUtils.getBase(fileName);
+          AlignFrame matchingAf = null;
+          AlignFrame[] afs = Desktop.instance.getAlignFrames();
+          boolean dontSkip = false;
+          for (AlignFrame af : afs)
           {
-            format = new IdentifyFile().identify(file, protocol);
+            String afFilename = af.fileName;
+            String afTitle = af.getTitle();
+            /**
+             * don't need to check the matching afFilename or afTitle has an
+             * alignment extenstion. It's obviously an alignment!
+             */
+            /*
+            if ((base.equals(FileUtils.getBase(afFilename))
+                    && ArgParserUtils.alignmentExtensions
+                            .contains(FileUtils.getExtension(afFilename)))
+                    || (base.equals(FileUtils.getBase(afTitle))
+                            && ArgParserUtils.alignmentExtensions
+                                    .contains(FileUtils
+                                            .getExtension(afTitle))))
+            */
+            if (base.equals(FileUtils.getBase(afFilename))
+                    || base.equals(FileUtils.getBase(afTitle)))
+            {
+              matchingAf = af;
+              break;
+            }
           }
-          // If features/annotations/tree file that hasn't been put into the
-          // baseInfoMap, look through titles of opened AlignFrames to add.
-          String ext = FileUtils.getExtension(fileName);
-          boolean isFeatures = ArgParserUtils.featuresExtensions
-                  .contains(ext);
-          boolean isAnnotations = ArgParserUtils.annotationsExtensions
-                  .contains(ext);
-          boolean isTree = ArgParserUtils.treeExtensions.contains(ext);
-          if (isFeatures || isAnnotations || isTree)
+          if (matchingAf != null)
           {
-            String base = FileUtils.getBase(fileName);
-            AlignFrame matchingAf = null;
-            AlignFrame[] afs = Desktop.instance.getAlignFrames();
-            boolean dontSkip = false;
-            for (AlignFrame af : afs)
+            if (isFeatures)
             {
-              String afFilename = af.fileName;
-              String afTitle = af.getTitle();
-              /**
-               * don't need to check the matching afFilename or afTitle has an
-               * alignment extenstion. It's obviously an alignment!
-               */
-              /*
-              if ((base.equals(FileUtils.getBase(afFilename))
-                      && ArgParserUtils.alignmentExtensions
-                              .contains(FileUtils.getExtension(afFilename)))
-                      || (base.equals(FileUtils.getBase(afTitle))
-                              && ArgParserUtils.alignmentExtensions
-                                      .contains(FileUtils
-                                              .getExtension(afTitle))))
-              */
-              if (base.equals(FileUtils.getBase(afFilename))
-                      || base.equals(FileUtils.getBase(afTitle)))
-              {
-                matchingAf = af;
-                break;
-              }
+              matchingAf.parseFeaturesFile(fileName,
+                      AppletFormatAdapter.checkProtocol(fileName));
+            }
+            else if (isAnnotations)
+            {
+              matchingAf.loadJalviewDataFile(fileName, null, null, null);
             }
-            if (matchingAf != null)
+            else if (isTree)
             {
-              if (isFeatures)
+              try
               {
-                matchingAf.parseFeaturesFile(fileName,
+                NewickFile nf = new NewickFile(fileName,
                         AppletFormatAdapter.checkProtocol(fileName));
-              }
-              else if (isAnnotations)
-              {
-                matchingAf.loadJalviewDataFile(fileName, null, null, null);
-              }
-              else if (isTree)
-              {
-                try
-                {
-                  NewickFile nf = new NewickFile(fileName,
-                          AppletFormatAdapter.checkProtocol(fileName));
-                  matchingAf.getViewport().setCurrentTree(matchingAf
-                          .showNewickTree(nf, fileName).getTree());
-                } catch (IOException e)
-                {
-                  jalview.bin.Console.warn(
-                          "Couldn't add potential tree '" + fileName + "'");
-                  dontSkip = true;
-                }
-              }
-              if (!dontSkip)
+                matchingAf.getViewport().setCurrentTree(
+                        matchingAf.showNewickTree(nf, fileName).getTree());
+              } catch (IOException e)
               {
-                // skip to next file
-                continue;
+                jalview.bin.Console.warn(
+                        "Couldn't add potential tree '" + fileName + "'");
+                dontSkip = true;
               }
             }
+            if (!dontSkip)
+            {
+              // skip to next file
+              continue;
+            }
           }
+        }
 
-          if (file instanceof File)
+        if (file instanceof File)
+        {
+          Platform.cacheFileData((File) file);
+        }
+        // new FileLoader().LoadFile(null, file, protocol, format);
+        AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(fileName,
+                protocol, format);
+        // now we add any associated files to the alignframe
+        if (baseInfoMap.containsKey(fileName) && baseInfoMap.get(fileName)
+                .getAssociatedFilesMap() != null)
+        {
+          BaseInfo bi = baseInfoMap.get(fileName);
+          if (bi.getAssociatedFilesMap().containsKey(Arg.FEATURES))
           {
-            Platform.cacheFileData((File) file);
+            String featuresfile = bi.getAssociatedFilesMap()
+                    .get(Arg.FEATURES);
+            af.parseFeaturesFile(featuresfile,
+                    AppletFormatAdapter.checkProtocol(featuresfile));
           }
-          // new FileLoader().LoadFile(null, file, protocol, format);
-          AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(fileName,
-                  protocol, format);
-          // now we add any associated files to the alignframe
-          if (baseInfoMap.containsKey(fileName) && baseInfoMap.get(fileName)
-                  .getAssociatedFilesMap() != null)
+          if (bi.getAssociatedFilesMap().containsKey(Arg.ANNOTATIONS))
           {
-            BaseInfo bi = baseInfoMap.get(fileName);
-            if (bi.getAssociatedFilesMap().containsKey(Arg.FEATURES))
-            {
-              String featuresfile = bi.getAssociatedFilesMap()
-                      .get(Arg.FEATURES);
-              af.parseFeaturesFile(featuresfile,
-                      AppletFormatAdapter.checkProtocol(featuresfile));
-            }
-            if (bi.getAssociatedFilesMap().containsKey(Arg.ANNOTATIONS))
+            String annotationsfile = bi.getAssociatedFilesMap()
+                    .get(Arg.ANNOTATIONS);
+            af.loadJalviewDataFile(annotationsfile, null, null, null);
+          }
+          if (bi.getAssociatedFilesMap().containsKey(Arg.TREE))
+          {
+            String treefile = bi.getAssociatedFilesMap().get(Arg.TREE);
+            try
             {
-              String annotationsfile = bi.getAssociatedFilesMap()
-                      .get(Arg.ANNOTATIONS);
-              af.loadJalviewDataFile(annotationsfile, null, null, null);
-            }
-            if (bi.getAssociatedFilesMap().containsKey(Arg.TREE))
+              NewickFile nf = new NewickFile(treefile,
+                      AppletFormatAdapter.checkProtocol(treefile));
+              af.getViewport().setCurrentTree(
+                      af.showNewickTree(nf, treefile).getTree());
+            } catch (IOException e)
             {
-              String treefile = bi.getAssociatedFilesMap().get(Arg.TREE);
-              try
-              {
-                NewickFile nf = new NewickFile(treefile,
-                        AppletFormatAdapter.checkProtocol(treefile));
-                af.getViewport().setCurrentTree(
-                        af.showNewickTree(nf, treefile).getTree());
-              } catch (IOException e)
-              {
-                jalview.bin.Console.warn(
-                        "Couldn't add potential tree '" + treefile + "'");
-              }
-
+              jalview.bin.Console.warn(
+                      "Couldn't add potential tree '" + treefile + "'");
             }
 
           }
 
         }
-      } catch (Exception ex)
+
+      }
+    } catch (Exception ex)
+    {
+
+      jalview.bin.Console.warn(
+              "Unexpected exception whilst handling drop to Desktop.", ex);
+      failed.add(file);
+      failed_exceptions.add(ex);
+      return false;
+    }
+    return failed.size() == 0; // redundant for 2.11.4.0 implementation
+  }
+  
+  @Override
+  public void drop(DropTargetDropEvent evt)
+  {
+    boolean success = true;
+    // JAL-1552 - acceptDrop required before getTransferable call for
+    // Java's Transferable for native dnd
+    evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
+    Transferable t = evt.getTransferable();
+    List<Object> files = new ArrayList<>();
+    List<DataSourceType> protocols = new ArrayList<>();
+
+    try
+    {
+      Desktop.transferFromDropTarget(files, protocols, evt, t);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+      success = false;
+    }
+    try
+    {
+      if (files != null)
       {
-        success = false;
+        List<Object> failed = new ArrayList<Object>();
+        List<Exception> failed_exceptions = new ArrayList<Exception>();
+        if (!showExperimental())
+        {
+          success = loadDroppedFiles(files, protocols, failed,
+                  failed_exceptions);
+        }
+        else
+        {
+          success = processAndLoadDroppedFiles(files, protocols, failed,
+                  failed_exceptions);
+        }
       }
+    } catch (Throwable ex)
+    {
+      jalview.bin.Console.warn(
+              "Unexpected exception whilst handling drop to Desktop.", ex);
+      success = false;
     }
     evt.dropComplete(success); // need this to ensure input focus is properly
                                // transfered to any new windows created
@@ -2945,11 +3021,11 @@ public class Desktop extends jalview.jbgui.GDesktop
 
     if (progressBars.get(Long.valueOf(id)) == null)
       return null;
+
     for (Component c : progressBars.get(Long.valueOf(id)).getComponents())
     {
       if (c.getClass() == JProgressBar.class)
-       return (JProgressBar) c;
+        return (JProgressBar) c;
     }
     return null;
   }
index 853d4aa..1ec4881 100755 (executable)
@@ -113,8 +113,9 @@ public class FontChooser extends GFontChooser
 
   /**
    * Creates a new FontChooser for a CutAndPasteTransfer
+   * 
    * @param cap
-  */
+   */
   public FontChooser(CutAndPasteTransfer cap)
   {
     oldFont = new Font("Monospaced", Font.PLAIN, 12);
@@ -131,7 +132,7 @@ public class FontChooser extends GFontChooser
     if (!isCapFont())
     {
       smoothFont.setSelected(ap.av.antiAlias);
-  
+
       /*
        * Enable 'scale protein as cDNA' in a SplitFrame view. The selection is
        * stored in the ViewStyle of both dna and protein Viewport. Also enable
@@ -289,6 +290,7 @@ public class FontChooser extends GFontChooser
   {
     return cap != null;
   }
+
   /**
    * DOCUMENT ME!
    */
index 702c4f4..7926efc 100644 (file)
@@ -101,12 +101,12 @@ public class PaSiMapPanel extends GPaSiMapPanel
    */
   public PaSiMapPanel(AlignmentPanel alignPanel, String modelName)
   {
-    super(8);  // dim = 8
+    super(8); // dim = 8
     this.av = alignPanel.av;
     this.ap = alignPanel;
     boolean nucleotide = av.getAlignment().isNucleotide();
 
-    //progressBar = new ProgressBar(statusPanel, statusBar);
+    // progressBar = new ProgressBar(statusPanel, statusBar);
 
     addInternalFrameListener(new InternalFrameAdapter()
     {
@@ -131,8 +131,7 @@ public class PaSiMapPanel extends GPaSiMapPanel
 
     ScoreModelI scoreModel = ScoreModels.getInstance()
             .getScoreModel(modelName, ap);
-    setPasimapModel(
-            new PaSiMapModel(av, seqs, nucleotide, scoreModel));
+    setPasimapModel(new PaSiMapModel(av, seqs, nucleotide, scoreModel));
     PaintRefresher.Register(this, av.getSequenceSetId());
 
     setRotatableCanvas(new RotatableCanvas(alignPanel));
@@ -143,8 +142,8 @@ public class PaSiMapPanel extends GPaSiMapPanel
   }
 
   /**
-   * Ensure references to potentially very large objects (the PaSiMap matrices) are
-   * nulled when the frame is closed
+   * Ensure references to potentially very large objects (the PaSiMap matrices)
+   * are nulled when the frame is closed
    */
   protected void close_actionPerformed()
   {
@@ -193,29 +192,31 @@ public class PaSiMapPanel extends GPaSiMapPanel
     progressBar.setProgressBar(message, progId);
     try
     {
-      SequenceGroup selGroup=av.getSelectionGroup();
-      
-      if (selGroup==null)
+      SequenceGroup selGroup = av.getSelectionGroup();
+
+      if (selGroup == null)
       {
         selGroup = new SequenceGroup(av.getAlignment().getSequences());
         selGroup.setStartRes(0);
-        selGroup.setEndRes(av.getAlignment().getWidth()-1);
+        selGroup.setEndRes(av.getAlignment().getWidth() - 1);
       }
-      
-      if (selGroup.getSize()>MAX_PASIMAP_SEQ)
+
+      if (selGroup.getSize() > MAX_PASIMAP_SEQ)
       {
-        int start = selGroup.getStartRes(),end=selGroup.getEndRes();
-          selGroup = new SequenceGroup(selGroup.getSequences().subList(0, MAX_PASIMAP_SEQ));
-          selGroup.setStartRes(start);
-          selGroup.setEndRes(end);
-          Console.warn("Truncated input sequences for PASIMAP analysis to "+MAX_PASIMAP_SEQ);
+        int start = selGroup.getStartRes(), end = selGroup.getEndRes();
+        selGroup = new SequenceGroup(
+                selGroup.getSequences().subList(0, MAX_PASIMAP_SEQ));
+        selGroup.setStartRes(start);
+        selGroup.setEndRes(end);
+        Console.warn("Truncated input sequences for PASIMAP analysis to "
+                + MAX_PASIMAP_SEQ);
       }
-      
+
       PairwiseAlignPanel pap = new PairwiseAlignPanel(av, selGroup, true,
               GAP_OPEN_COST, GAP_EXTEND_COST, false, null);
       pap.setDiscardAlignments(true);
       pap.setQuiet(true);
-      
+
       System.out.println(pap != null);
       setPairwiseAlignPanel(pap);
       getPasimapModel().calculate(pap);
@@ -241,9 +242,9 @@ public class PaSiMapPanel extends GPaSiMapPanel
     repaint();
     if (getParent() == null)
     {
-      Desktop.addInternalFrame(
-              this, MessageManager.formatMessage("label.calc_title",
-                      "PaSiMap", ap.alignFrame.getTitle()),
+      Desktop.addInternalFrame(this,
+              MessageManager.formatMessage("label.calc_title", "PaSiMap",
+                      ap.alignFrame.getTitle()),
               475, 450);
       this.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
     }
@@ -251,8 +252,8 @@ public class PaSiMapPanel extends GPaSiMapPanel
   }
 
   /**
-   * Updates the PaSiMap display after a change of component to use for x, y or z
-   * axis
+   * Updates the PaSiMap display after a change of component to use for x, y or
+   * z axis
    */
   @Override
   protected void doDimensionChange()
@@ -270,8 +271,8 @@ public class PaSiMapPanel extends GPaSiMapPanel
   }
 
   /**
-   * Sets the selected checkbox item index for PaSiMap dimension (1, 2, 3...) for
-   * the given axis (X/Y/Z)
+   * Sets the selected checkbox item index for PaSiMap dimension (1, 2, 3...)
+   * for the given axis (X/Y/Z)
    * 
    * @param index
    * @param axis
@@ -323,8 +324,8 @@ public class PaSiMapPanel extends GPaSiMapPanel
   }
 
   /**
-   * If available, shows the data which formed the inputs for the PaSiMap as a new
-   * alignment
+   * If available, shows the data which formed the inputs for the PaSiMap as a
+   * new alignment
    */
   @Override
   public void originalSeqData_actionPerformed()
@@ -452,7 +453,7 @@ public class PaSiMapPanel extends GPaSiMapPanel
     }
   }
 
-  public void makePaSiMapImage(ImageMaker.TYPE type)  throws Exception
+  public void makePaSiMapImage(ImageMaker.TYPE type) throws Exception
   {
     int width = getRotatableCanvas().getWidth();
     int height = getRotatableCanvas().getHeight();
@@ -612,7 +613,8 @@ public class PaSiMapPanel extends GPaSiMapPanel
     {
       cap.setText(getPasimapModel().getAlignmentOutput());
       Desktop.addInternalFrame(cap, MessageManager.formatMessage(
-       "label.pairwise_alignment_for_params", new String[] { this.getTitle() }), 500, 500);
+              "label.pairwise_alignment_for_params", new String[]
+              { this.getTitle() }), 500, 500);
     } catch (OutOfMemoryError oom)
     {
       new OOMWarning("exporting pairwise alignments", oom);
@@ -680,7 +682,7 @@ public class PaSiMapPanel extends GPaSiMapPanel
   public void updateProgressBar(int lengthOfTask, int progress)
   {
     JProgressBar pBar = progressBar.getProgressBar(progId);
-    if (pBar==null)
+    if (pBar == null)
     {
       return;
     }
@@ -692,22 +694,24 @@ public class PaSiMapPanel extends GPaSiMapPanel
     }
     updateProgressBar(progress);
   }
+
   public void updateProgressBar(int progress)
   {
     JProgressBar pBar = progressBar.getProgressBar(progId);
-    
-    if (pBar==null)
+
+    if (pBar == null)
     {
       return;
     }
-    
+
     pBar.setValue(progress);
     pBar.repaint();
   }
 
   /**
-   * adds a listener for a pairwise alignment panel's progress
-   * TODO: generalise for anything that can report progress ?
+   * adds a listener for a pairwise alignment panel's progress TODO: generalise
+   * for anything that can report progress ?
+   * 
    * @param pap
    */
   public void setPairwiseAlignPanel(PairwiseAlignPanel pap)
@@ -794,8 +798,8 @@ public class PaSiMapPanel extends GPaSiMapPanel
   }
 
   /**
-   * Answers the selected checkbox item index for PaSiMap dimension for the X, Y or
-   * Z axis of the display
+   * Answers the selected checkbox item index for PaSiMap dimension for the X, Y
+   * or Z axis of the display
    * 
    * @param axis
    * @return
index 3e8fb02..77ec0f5 100755 (executable)
@@ -47,7 +47,7 @@ public class PairwiseAlignPanel extends GPairwiseAlignPanel
 
   private float[][] scores;
 
-  private float[][] alignmentScores;   // scores used by PaSiMap
+  private float[][] alignmentScores; // scores used by PaSiMap
 
   private int GAP_OPEN_COST;
 
@@ -60,7 +60,7 @@ public class PairwiseAlignPanel extends GPairwiseAlignPanel
   private String alignmentOutput;
 
   private boolean quiet;
+
   private boolean discardAlignments;
 
   private boolean endGaps;
@@ -73,12 +73,13 @@ public class PairwiseAlignPanel extends GPairwiseAlignPanel
   private int total;
 
   private int progress;
-  
+
   private SequenceGroup selection;
+
   /**
    * input sequences
    */
-  private SequenceI[] seqs=null;
+  private SequenceI[] seqs = null;
 
   private ScoreMatrix scoreMatrix;
 
@@ -87,23 +88,31 @@ public class PairwiseAlignPanel extends GPairwiseAlignPanel
    * 
    * @param viewport
    *          contains selected sequences to align
-   * @param endGaps ~ toggle gaps and the beginning and end of sequences
+   * @param endGaps
+   *          ~ toggle gaps and the beginning and end of sequences
    */
   public PairwiseAlignPanel(AlignmentViewport viewport)
   {
-    this(viewport, null, false, 120, 20, true, null);  // default penalties used in AlignSeq
+    this(viewport, null, false, 120, 20, true, null); // default penalties used
+                                                      // in AlignSeq
   }
+
   public PairwiseAlignPanel(AlignmentViewport viewport, ScoreMatrix params)
   {
-    this(viewport, null, false, 120, 20, true,params); // default penalties used in AlignSeq
+    this(viewport, null, false, 120, 20, true, params); // default penalties
+                                                        // used in AlignSeq
   }
-  public PairwiseAlignPanel(AlignmentViewport viewport, boolean endGaps, int gapOpenCost, int gapExtendCost)
+
+  public PairwiseAlignPanel(AlignmentViewport viewport, boolean endGaps,
+          int gapOpenCost, int gapExtendCost)
   {
     this(viewport, null, endGaps, gapOpenCost, gapExtendCost, true, null);
   }
 
   /**
-   * Create a new pairwise alignpanel with specified parameters and score model, and optionally start the calculation
+   * Create a new pairwise alignpanel with specified parameters and score model,
+   * and optionally start the calculation
+   * 
    * @param viewport
    * @param selection
    * @param endGaps
@@ -112,8 +121,9 @@ public class PairwiseAlignPanel extends GPairwiseAlignPanel
    * @param run
    * @param scoreMatrix
    */
-  public PairwiseAlignPanel(AlignmentViewport viewport, SequenceGroup selection, boolean  endGaps,
-          int gapOpenCost, int gapExtendCost, boolean run, ScoreMatrix scoreMatrix)
+  public PairwiseAlignPanel(AlignmentViewport viewport,
+          SequenceGroup selection, boolean endGaps, int gapOpenCost,
+          int gapExtendCost, boolean run, ScoreMatrix scoreMatrix)
   {
     super();
     this.av = viewport;
@@ -123,23 +133,25 @@ public class PairwiseAlignPanel extends GPairwiseAlignPanel
     this.selection = selection;
     this.total = MiscMath.combinations(av.getAlignment().getHeight(), 2);
     this.scoreMatrix = scoreMatrix;
-    if (run) {
+    if (run)
+    {
       calculate();
     }
   }
-  
+
   public void calculate()
   {
     calculate(scoreMatrix);
   }
-  public void calculate (ScoreMatrix sm)
+
+  public void calculate(ScoreMatrix sm)
   {
 
     StringBuilder sb = new StringBuilder(1024);
 
     sequences = new Vector<SequenceI>();
     String[] seqStrings;
-    seqs=null;
+    seqs = null;
 
     if (selection != null)
     {
@@ -169,7 +181,7 @@ public class PairwiseAlignPanel extends GPairwiseAlignPanel
         seqs = av.getAlignment().getSequencesArray();
       }
     }
-    
+
     String type = (av.getAlignment().isNucleotide()) ? AlignSeq.DNA
             : AlignSeq.PEP;
 
@@ -177,7 +189,7 @@ public class PairwiseAlignPanel extends GPairwiseAlignPanel
     float[][] alignmentScores = new float[seqs.length][seqs.length];
     double totscore = 0D;
     int count = seqs.length;
-    
+
     boolean first = true;
 
     progress = 0;
@@ -192,11 +204,12 @@ public class PairwiseAlignPanel extends GPairwiseAlignPanel
         AlignSeq as = new AlignSeq(seqs[i], seqStrings[i], seqs[j],
                 seqStrings[j], type, GAP_OPEN_COST, GAP_EXTEND_COST);
 
-        if (sm != null) {
-                       as.setScoreMatrix(sm);
-               }
+        if (sm != null)
+        {
+          as.setScoreMatrix(sm);
+        }
 
-               if (as.s1str.length() == 0 || as.s2str.length() == 0)
+        if (as.s1str.length() == 0 || as.s2str.length() == 0)
         {
           continue;
         }
@@ -219,7 +232,8 @@ public class PairwiseAlignPanel extends GPairwiseAlignPanel
           sb.append(DASHES);
         }
         first = false;
-        if (!discardAlignments) {
+        if (!discardAlignments)
+        {
           as.printAlignment(System.out);
         }
         scores[i][j] = as.getMaxScore() / as.getASeq1().length;
@@ -243,7 +257,7 @@ public class PairwiseAlignPanel extends GPairwiseAlignPanel
     this.scores = scores;
     this.alignmentScores = alignmentScores;
 
-    if (count > 2 && !quiet) 
+    if (count > 2 && !quiet)
     {
       printScoreMatrix(seqs, scores, totscore);
     }
@@ -344,24 +358,29 @@ public class PairwiseAlignPanel extends GPairwiseAlignPanel
   {
     return progress;
   }
+
   public SequenceI[] getInputSequences()
   {
     return seqs;
   }
+
   /**
-   * Set to true to suppress output of progress to Console.stdout or GUI 
-   * @param quiet 
+   * Set to true to suppress output of progress to Console.stdout or GUI
+   * 
+   * @param quiet
    */
   public void setQuiet(boolean quiet)
   {
-    this.quiet=quiet;    
+    this.quiet = quiet;
   }
+
   /**
    * set this if you are only interested in final alignment scores
+   * 
    * @param discard
    */
   public void setDiscardAlignments(boolean discard)
   {
-    discardAlignments=discard;
+    discardAlignments = discard;
   }
 }
index a47a0a1..cc60400 100644 (file)
@@ -1973,7 +1973,6 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
     AlignmentUtils.addReferenceAnnotations(candidates, alignment, null);
         
     if(AlignmentUtils.isSSAnnotationPresent(candidates)) {
-      
       restartSSConsensusWorker();
       ap.validateAnnotationDimensions(true);
       ap.fontChanged();
@@ -1982,23 +1981,23 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
       restartSSConsensusWorker();
       //ap.alignFrame.getViewport().getCalcManager().restartWorkers();
     }
-        
   }
-  
-  
-  private void restartSSConsensusWorker() {
-    
-    List<AlignCalcWorkerI> workers = ap.alignFrame.getViewport().getCalcManager()
-            .getRegisteredWorkersOfClass(SecondaryStructureConsensusThread.class);
-    if (!workers.isEmpty()) {
-        
-      ap.alignFrame.getViewport().getCalcManager().startWorker(workers.remove(0));
+
+  private void restartSSConsensusWorker()
+  {
+
+    List<AlignCalcWorkerI> workers = ap.alignFrame.getViewport()
+            .getCalcManager().getRegisteredWorkersOfClass(
+                    SecondaryStructureConsensusThread.class);
+    if (!workers.isEmpty())
+    {
+
+      ap.alignFrame.getViewport().getCalcManager()
+              .startWorker(workers.remove(0));
 
     }
-        
+
   }
-  
-  
 
   protected void makeReferenceSeq_actionPerformed(ActionEvent actionEvent)
   {
index 73cd496..9d2cb2c 100644 (file)
@@ -274,7 +274,7 @@ public class ProgressBar implements IProgressIndicator
   public JProgressBar getProgressBar(long id)
   {
     Container progBar = progressBars.get(id);
-    if (progBar==null || progBar.getComponentCount()==0)
+    if (progBar == null || progBar.getComponentCount() == 0)
     {
       return null;
     }
index 5261b23..03d8d87 100755 (executable)
@@ -119,10 +119,13 @@ public class RotatableCanvas extends JPanel
   private Point[] axisEndPoints;
 
   // fields for 'select rectangle' (JAL-1124)
-   int rectx1;
-   int recty1;
-   int rectx2;
-   int recty2;
+  int rectx1;
+
+  int recty1;
+
+  int rectx2;
+
+  int recty2;
 
   AlignmentViewport av;
 
@@ -405,11 +408,12 @@ public class RotatableCanvas extends JPanel
       }
     }
     // //Now the rectangle
-     if (rectx2 != -1 && recty2 != -1) {
-     g.setColor(Color.white);
-    
-     g.drawRect(rectx1,recty1,rectx2-rectx1,recty2-recty1);
-     }
+    if (rectx2 != -1 && recty2 != -1)
+    {
+      g.setColor(Color.white);
+
+      g.drawRect(rectx1, recty1, rectx2 - rectx1, recty2 - recty1);
+    }
   }
 
   /**
@@ -566,10 +570,10 @@ public class RotatableCanvas extends JPanel
     mouseX = x;
     mouseY = y;
 
-     rectx1 = x;
-     recty1 = y;
-     rectx2 = -1;
-     recty2 = -1;
+    rectx1 = x;
+    recty1 = y;
+    rectx2 = -1;
+    recty2 = -1;
 
     SequenceI found = findSequenceAtPoint(x, y);
 
@@ -634,12 +638,12 @@ public class RotatableCanvas extends JPanel
     // Check if this is a rectangle drawing drag
     if ((evt.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) != 0)
     {
-       rectx2 = evt.getX();
-       recty2 = evt.getY();
-       if ((rectx2 != -1) && (recty2 != -1))
-       {
-         rectSelect(rectx1, recty1, rectx2, recty2);
-       }
+      rectx2 = evt.getX();
+      recty2 = evt.getY();
+      if ((rectx2 != -1) && (recty2 != -1))
+      {
+        rectSelect(rectx1, recty1, rectx2, recty2);
+      }
     }
     else
     {
@@ -649,7 +653,7 @@ public class RotatableCanvas extends JPanel
       mouseY = yPos;
       // findWidths();
     }
-      repaint();
+    repaint();
   }
 
   @Override
@@ -753,12 +757,10 @@ public class RotatableCanvas extends JPanel
     {
       SequencePoint sp = sequencePoints.get(i);
       int tmp1 = (int) (((sp.coord.x - centre[0]) * getScaleFactor())
-              * (getWidth() / 3.15)
-              + (getWidth() / 2.0));
+              * (getWidth() / 3.15) + (getWidth() / 2.0));
       float pre1 = ((sp.coord.x - centre[0]) * getScaleFactor());
       int tmp2 = (int) (((sp.coord.y - centre[1]) * getScaleFactor())
-              * (getHeight() / 1.70)
-              + (getHeight() / 2.0));
+              * (getHeight() / 1.70) + (getHeight() / 2.0));
       float pre2 = ((sp.coord.y - centre[1]) * getScaleFactor());
 
       if ((tmp1 > x1) && (tmp1 < x2) && (tmp2 > y1) && (tmp2 < y2))
index 9658a92..ce043fe 100644 (file)
@@ -1347,8 +1347,9 @@ public class StructureChooser extends GStructureChooser
           String pdbFilename = selectedPdbFileName;
           // TODO - tidy up this ugly hack so we call launchStructureViewer too
           StructureChooser.openStructureFileForSequence(ssm, sc, ap,
-                  selectedSequence, true, pdbFilename, tft, paeFilename,false,
-                  true,false,getTargetedStructureViewer(ssm).getViewerType());
+                  selectedSequence, true, pdbFilename, tft, paeFilename,
+                  false, true, false,
+                  getTargetedStructureViewer(ssm).getViewerType());
         }
         SwingUtilities.invokeLater(new Runnable()
         {
@@ -1820,7 +1821,9 @@ public class StructureChooser extends GStructureChooser
    * @param forceHeadless
    * @param showRefAnnotations
    * @param doXferSettings
-   * @param viewerType - when not null means the viewer will be opened, providing forceHeadless/headless is not true
+   * @param viewerType
+   *          - when not null means the viewer will be opened, providing
+   *          forceHeadless/headless is not true
    * @return
    */
   public static StructureViewer openStructureFileForSequence(
@@ -1845,7 +1848,7 @@ public class StructureChooser extends GStructureChooser
       ssm = ap.getStructureSelectionManager();
       StructureSelectionManager.doConfigureStructurePrefs(ssm);
     }
-    
+
     PDBEntry fileEntry = new AssociatePdbFileWithSeq().associatePdbWithSeq(
             sFilename, DataSourceType.FILE, seq, prompt, Desktop.instance,
             tft, paeFilename, doXferSettings);
@@ -1863,7 +1866,8 @@ public class StructureChooser extends GStructureChooser
 
     sc.mainFrame.dispose();
 
-    // TODO should honor preferences - only show reference annotation that is requested - JAL-4415 JAL-3124
+    // TODO should honor preferences - only show reference annotation that is
+    // requested - JAL-4415 JAL-3124
     if (showRefAnnotations)
     {
       showReferenceAnnotationsForSequence(ap.alignFrame, seq);
index 333f995..2bd5a91 100644 (file)
@@ -426,8 +426,9 @@ public class ThreeDBStructureChooserQuerySource
       String modelPage = humanUrl < 1 ? null
               : (String) restable.getValueAt(row, humanUrl);
 
-      String modelCategory = idx_mcat < 1 ? null :(String) restable.getValueAt(row,idx_mcat); 
-              
+      String modelCategory = idx_mcat < 1 ? null
+              : (String) restable.getValueAt(row, idx_mcat);
+
       String strucFormat = restable.getValueAt(row, modelformat).toString();
 
       SequenceI selectedSeq = (SequenceI) restable.getValueAt(row,
index fa9626b..d1ced96 100755 (executable)
@@ -210,7 +210,7 @@ public class GAlignFrame extends JInternalFrame
 
   protected JCheckBoxMenuItem showGroupConservation = new JCheckBoxMenuItem();
 
-  protected JCheckBoxMenuItem showConsensusHistogram = new JCheckBoxMenuItem();  
+  protected JCheckBoxMenuItem showConsensusHistogram = new JCheckBoxMenuItem();
 
   protected JCheckBoxMenuItem showSequenceLogo = new JCheckBoxMenuItem();
 
@@ -925,8 +925,8 @@ public class GAlignFrame extends JInternalFrame
       {
         showConsensusHistogram_actionPerformed(e);
       }
-
     });    
+
     showSequenceLogo
             .setText(MessageManager.getString("label.show_consensus_logo"));
     showSequenceLogo.addActionListener(new ActionListener()
@@ -1887,7 +1887,7 @@ public class GAlignFrame extends JInternalFrame
          
       }
     });
-    
+
     JMenu exportImageMenu = new JMenu(
             MessageManager.getString("label.export_image"));
     JMenu fileMenu = new JMenu(MessageManager.getString("action.file"));
@@ -2957,6 +2957,6 @@ public class GAlignFrame extends JInternalFrame
           ButtonGroup ssButtonGroup)
   {
     // TODO Auto-generated method stub
-    
+
   }
 }
index 5daea78..106ad7c 100755 (executable)
@@ -198,7 +198,7 @@ public class GCutAndPasteTransfer extends JInternalFrame
       @Override
       public void actionPerformed(ActionEvent e)
       {
-       fontSizeMenu_actionPerformed(e);
+        fontSizeMenu_actionPerformed(e);
       }
     });
     copyItem.setText(MessageManager.getString("action.copy"));
@@ -256,6 +256,7 @@ public class GCutAndPasteTransfer extends JInternalFrame
 
   /**
    * shows a menu for changing the font
+   * 
    * @param e
    */
   public void fontSizeMenu_actionPerformed(ActionEvent e)
index 3d606e3..90b8024 100755 (executable)
@@ -48,7 +48,7 @@ import jalview.util.Platform;
 public class GDesktop extends JFrame
 {
 
-  protected static JMenu windowMenu = new JMenu();
+  protected static JMenu windowMenu = null;
 
   JMenuBar desktopMenubar = new JMenuBar();
 
@@ -110,6 +110,13 @@ public class GDesktop extends JFrame
   public GDesktop()
   {
     super();
+
+    // instantiate here to set same context (e.g. font) as other menus
+    if (windowMenu == null)
+    {
+      windowMenu = new JMenu();
+    }
+
     try
     {
       jbInit();
index ec5b209..65394ed 100755 (executable)
@@ -85,6 +85,7 @@ public class GPCAPanel extends JInternalFrame
       zCombobox.addItem("dim " + i);
     }
   }
+
   public GPCAPanel(int dim)
   {
     try
index 369a005..3c7fb5a 100755 (executable)
@@ -85,6 +85,7 @@ public class GPaSiMapPanel extends JInternalFrame
       zCombobox.addItem("dim " + i);
     }
   }
+
   public GPaSiMapPanel(int dim)
   {
     try
@@ -223,8 +224,8 @@ public class GPaSiMapPanel extends JInternalFrame
       }
     });
     JMenuItem outputAlignment = new JMenuItem();
-    outputAlignment.setText(
-      MessageManager.getString("label.output_alignment"));
+    outputAlignment
+            .setText(MessageManager.getString("label.output_alignment"));
     outputAlignment.addActionListener(new ActionListener()
     {
       @Override
index e574327..c644e3c 100755 (executable)
@@ -160,7 +160,7 @@ public class GPreferences extends JPanel
 
   protected JCheckBox conservation = new JCheckBox();
 
-  protected JCheckBox identity = new JCheckBox();  
+  protected JCheckBox identity = new JCheckBox();
 
   protected JCheckBox ssConsensus = new JCheckBox();
 
index 7ae4b94..f767444 100755 (executable)
@@ -123,12 +123,12 @@ public class Matrix implements MatrixI
       if (row != null)
       {
         value[i] = new double[row.length];
-       int j = 0;
-       for (float oldValue : row)
-       {
-         value[i][j] = oldValue;
-         j++;
-       }
+        int j = 0;
+        for (float oldValue : row)
+        {
+          value[i][j] = oldValue;
+          j++;
+        }
       }
       i++;
     }
@@ -191,10 +191,11 @@ public class Matrix implements MatrixI
          */
         for (int k = 0; k < in.width(); k++)
         {
-         if (!Double.isNaN(in.getValue(i,k)) && !Double.isNaN(this.value[k][j]))
-         {
+          if (!Double.isNaN(in.getValue(i, k))
+                  && !Double.isNaN(this.value[k][j]))
+          {
             tmp[i][j] += (in.getValue(i, k) * this.value[k][j]);
-         }
+          }
         }
       }
     }
@@ -833,10 +834,10 @@ public class Matrix implements MatrixI
   }
 
   /**
-  * returns the matrix as a double[][] array
-  *
-  * @return
-  */
+   * returns the matrix as a double[][] array
+   *
+   * @return
+   */
   @Override
   public double[][] asArray()
   {
@@ -1018,7 +1019,8 @@ public class Matrix implements MatrixI
   /**
    * Add d to all entries of this matrix
    * 
-   * @param d ~ value to add
+   * @param d
+   *          ~ value to add
    */
   @Override
   public void add(double d)
@@ -1086,7 +1088,7 @@ public class Matrix implements MatrixI
   }
 
   /**
-   * Returns a copy in which  every value in the matrix is its absolute
+   * Returns a copy in which every value in the matrix is its absolute
    * 
    * @return
    */
@@ -1122,7 +1124,7 @@ public class Matrix implements MatrixI
     {
       if (row != null)
       {
-       mean[i++] = MiscMath.mean(row);
+        mean[i++] = MiscMath.mean(row);
       }
     }
     return mean;
@@ -1142,17 +1144,17 @@ public class Matrix implements MatrixI
       double[] column = getColumn(j);
       if (column != null)
       {
-       mean[j] = MiscMath.mean(column);
+        mean[j] = MiscMath.mean(column);
       }
     }
     return mean;
   }
 
   /**
-  * return a flattened matrix containing the sum of each column
-  *
-  * @return
-  */
+   * return a flattened matrix containing the sum of each column
+   *
+   * @return
+   */
   @Override
   public double[] sumCol()
   {
@@ -1162,17 +1164,17 @@ public class Matrix implements MatrixI
       double[] column = getColumn(j);
       if (column != null)
       {
-       sum[j] = MiscMath.sum(column);
+        sum[j] = MiscMath.sum(column);
       }
-    } 
+    }
     return sum;
   }
 
   /**
-  * returns the mean value of the complete matrix
-  *
-  * @return
-  */
+   * returns the mean value of the complete matrix
+   *
+   * @return
+   */
   @Override
   public double mean()
   {
@@ -1182,47 +1184,51 @@ public class Matrix implements MatrixI
     {
       for (double col : row)
       {
-       if (!Double.isNaN(col))
-       {
-         sum += col;
-       } else {
-         nanCount++;
-       }
+        if (!Double.isNaN(col))
+        {
+          sum += col;
+        }
+        else
+        {
+          nanCount++;
+        }
       }
     }
     return sum / (double) (this.rows * this.cols - nanCount);
   }
 
   /**
-  * fills up a diagonal matrix with its transposed copy
-  * !other side should be filled with 0
-  * !keeps Double.NaN found in either side
-  *
-  * TODO check on which side it was diagonal and only do calculations for the other side
-  */
+   * fills up a diagonal matrix with its transposed copy !other side should be
+   * filled with 0 !keeps Double.NaN found in either side
+   *
+   * TODO check on which side it was diagonal and only do calculations for the
+   * other side
+   */
   @Override
   public void fillDiagonal()
   {
     int n = this.rows;
     int m = this.cols;
-    MatrixI copy = this.transpose();   // goes through each element in the matrix and
-    for (int i = 0; i < n; i++)                // adds the value in the transposed copy to the original value
+    MatrixI copy = this.transpose(); // goes through each element in the matrix
+                                     // and
+    for (int i = 0; i < n; i++) // adds the value in the transposed copy to the
+                                // original value
     {
       for (int j = 0; j < m; j++)
       {
-       if (i != j)
-       {
-         this.addValue(i, j, copy.getValue(i,j));
-       }
+        if (i != j)
+        {
+          this.addValue(i, j, copy.getValue(i, j));
+        }
       }
     }
   }
 
   /**
-  * counts the number of Double.NaN in the matrix
-  *
-  * @return
-  */
+   * counts the number of Double.NaN in the matrix
+   *
+   * @return
+   */
   @Override
   public int countNaN()
   {
@@ -1231,117 +1237,139 @@ public class Matrix implements MatrixI
     {
       for (int j = 0; j < this.cols; j++)
       {
-       if (Double.isNaN(this.getValue(i,j)))
-       {
-         NaN++;
-       }
+        if (Double.isNaN(this.getValue(i, j)))
+        {
+          NaN++;
+        }
       }
     }
     return NaN;
   }
 
   /**
-  * performs an element-wise addition of this matrix by another matrix ~ this - m
-  * @param m ~ other matrix
-  *
-  * @return
-  */
+   * performs an element-wise addition of this matrix by another matrix ~ this -
+   * m
+   * 
+   * @param m
+   *          ~ other matrix
+   *
+   * @return
+   */
   @Override
   public MatrixI add(MatrixI m)
   {
     if (m.width() != cols || m.height() != rows)
     {
-      throw new IllegalArgumentException("Can't add a " + m.height() + "x" + m.width() + " to a " + this.rows + "x" + this.cols + " matrix");
+      throw new IllegalArgumentException(
+              "Can't add a " + m.height() + "x" + m.width() + " to a "
+                      + this.rows + "x" + this.cols + " matrix");
     }
     double[][] tmp = new double[this.rows][this.cols];
     for (int i = 0; i < this.rows; i++)
     {
       for (int j = 0; j < this.cols; j++)
       {
-       tmp[i][j] = this.getValue(i,j) + m.getValue(i,j);
+        tmp[i][j] = this.getValue(i, j) + m.getValue(i, j);
       }
     }
     return new Matrix(tmp);
   }
 
   /**
-  * performs an element-wise subtraction of this matrix by another matrix ~ this - m
-  * @param m ~ other matrix
-  *
-  * @return
-  */
+   * performs an element-wise subtraction of this matrix by another matrix ~
+   * this - m
+   * 
+   * @param m
+   *          ~ other matrix
+   *
+   * @return
+   */
   @Override
   public MatrixI subtract(MatrixI m)
   {
     if (m.width() != cols || m.height() != rows)
     {
-      throw new IllegalArgumentException("Can't subtract a " + m.height() + "x" + m.width() + " from a " + this.rows + "x" + this.cols + " matrix");
+      throw new IllegalArgumentException("Can't subtract a " + m.height()
+              + "x" + m.width() + " from a " + this.rows + "x" + this.cols
+              + " matrix");
     }
     double[][] tmp = new double[this.rows][this.cols];
     for (int i = 0; i < this.rows; i++)
     {
       for (int j = 0; j < this.cols; j++)
       {
-       tmp[i][j] = this.getValue(i,j) -  m.getValue(i,j);
+        tmp[i][j] = this.getValue(i, j) - m.getValue(i, j);
       }
     }
     return new Matrix(tmp);
   }
 
   /**
-  * performs an element-wise multiplication of this matrix by another matrix ~ this * m
-  * @param m ~ other matrix
-  *
-  * @return
-  */
+   * performs an element-wise multiplication of this matrix by another matrix ~
+   * this * m
+   * 
+   * @param m
+   *          ~ other matrix
+   *
+   * @return
+   */
   @Override
   public MatrixI elementwiseMultiply(MatrixI m)
   {
     if (m.width() != cols || m.height() != rows)
     {
-      throw new IllegalArgumentException("Can't multiply a " + this.rows + "x" + this.cols + " by a " + m.height() + "x" + m.width() + " matrix");
+      throw new IllegalArgumentException(
+              "Can't multiply a " + this.rows + "x" + this.cols + " by a "
+                      + m.height() + "x" + m.width() + " matrix");
     }
     double[][] tmp = new double[this.rows][this.cols];
     for (int i = 0; i < this.rows; i++)
     {
       for (int j = 0; j < this.cols; j++)
       {
-        tmp[i][j] = this.getValue(i, j) * m.getValue(i,j);
+        tmp[i][j] = this.getValue(i, j) * m.getValue(i, j);
       }
     }
     return new Matrix(tmp);
   }
 
   /**
-  * performs an element-wise division of this matrix by another matrix ~ this / m
-  * @param m ~ other matrix
-  *
-  * @return
-  */
+   * performs an element-wise division of this matrix by another matrix ~ this /
+   * m
+   * 
+   * @param m
+   *          ~ other matrix
+   *
+   * @return
+   */
   @Override
   public MatrixI elementwiseDivide(MatrixI m)
   {
     if (m.width() != cols || m.height() != rows)
     {
-      throw new IllegalArgumentException("Can't divide a " + this.rows + "x" + this.cols + " by a " + m.height() + "x" + m.width() + " matrix");
+      throw new IllegalArgumentException(
+              "Can't divide a " + this.rows + "x" + this.cols + " by a "
+                      + m.height() + "x" + m.width() + " matrix");
     }
     double[][] tmp = new double[this.rows][this.cols];
     for (int i = 0; i < this.rows; i++)
     {
       for (int j = 0; j < this.cols; j++)
       {
-        tmp[i][j] = this.getValue(i, j) / m.getValue(i,j);
+        tmp[i][j] = this.getValue(i, j) / m.getValue(i, j);
       }
     }
     return new Matrix(tmp);
   }
 
   /**
-  * calculate the root-mean-square for tow matrices
-  * @param m ~ other matrix
-  *
-  * @return
-  */
+   * calculate the root-mean-square for tow matrices
+   * 
+   * @param m
+   *          ~ other matrix
+   *
+   * @return
+   */
   @Override
   public double rmsd(MatrixI m)
   {
@@ -1351,10 +1379,10 @@ public class Matrix implements MatrixI
   }
 
   /**
-  * calculates the Frobenius norm of this matrix
-  *
-  * @return
-  */
+   * calculates the Frobenius norm of this matrix
+   *
+   * @return
+   */
   @Override
   public double norm()
   {
@@ -1363,17 +1391,17 @@ public class Matrix implements MatrixI
     {
       for (double val : row)
       {
-       result += Math.pow(val, 2);
+        result += Math.pow(val, 2);
       }
     }
     return Math.sqrt(result);
   }
 
   /**
-  * returns the sum of all values in this matrix
-  *
-  * @return
-  */
+   * returns the sum of all values in this matrix
+   *
+   * @return
+   */
   @Override
   public double sum()
   {
@@ -1382,24 +1410,28 @@ public class Matrix implements MatrixI
     {
       for (double val : row)
       {
-       sum += (Double.isNaN(val)) ? 0.0 : val;
+        sum += (Double.isNaN(val)) ? 0.0 : val;
       }
     }
     return sum;
   }
 
   /**
-  * returns the sum-product of this matrix with vector v
-  * @param v ~ vector
-  *
-  * @return
-  */
+   * returns the sum-product of this matrix with vector v
+   * 
+   * @param v
+   *          ~ vector
+   *
+   * @return
+   */
   @Override
   public double[] sumProduct(double[] v)
   {
     if (v.length != cols)
     {
-      throw new IllegalArgumentException("Vector and matrix do not have the same dimension! (" + v.length + " != " + cols + ")");
+      throw new IllegalArgumentException(
+              "Vector and matrix do not have the same dimension! ("
+                      + v.length + " != " + cols + ")");
     }
     double[] result = new double[rows];
     for (int i = 0; i < rows; i++)
@@ -1408,7 +1440,7 @@ public class Matrix implements MatrixI
       double sum = 0;
       for (int j = 0; j < row.length; j++)
       {
-       sum += row[j] * v[j];
+        sum += row[j] * v[j];
       }
       result[i] = sum;
     }
@@ -1416,20 +1448,20 @@ public class Matrix implements MatrixI
   }
 
   /**
-  * mirrors columns of the matrix
-  *
-  * @return
-  */
+   * mirrors columns of the matrix
+   *
+   * @return
+   */
   @Override
   public MatrixI mirrorCol()
   {
     double[][] result = new double[rows][cols];
     for (int i = 0; i < rows; i++)
     {
-      int k = cols - 1;        // reverse col
+      int k = cols - 1; // reverse col
       for (int j = 0; j < cols; j++)
       {
-       result[i][k--] = this.getValue(i,j);
+        result[i][k--] = this.getValue(i, j);
       }
     }
     MatrixI resultMatrix = new Matrix(result);
index 7faac73..b932b33 100644 (file)
@@ -61,10 +61,10 @@ public interface MatrixI
   void setValue(int i, int j, double d);
 
   /**
-  * Returns the matrix as a double[][] array
-  *
-  * @return
-  */
+   * Returns the matrix as a double[][] array
+   *
+   * @return
+   */
   double[][] asArray();
 
   /**
@@ -175,10 +175,11 @@ public interface MatrixI
   void multiply(double d);
 
   /**
-  * Add d to all entries of this matrix
-  *
-  * @param d ~ value to add
-  */
+   * Add d to all entries of this matrix
+   *
+   * @param d
+   *          ~ value to add
+   */
   void add(double d);
 
   /**
@@ -193,7 +194,7 @@ public interface MatrixI
   boolean equals(MatrixI m2, double delta);
 
   /**
-   * Returns a copy in which  every value in the matrix is its absolute
+   * Returns a copy in which every value in the matrix is its absolute
    */
   MatrixI absolute();
 
@@ -208,110 +209,122 @@ public interface MatrixI
   double[] meanCol();
 
   /**
-  * Returns a flattened matrix containing the sum of each column
-  *
-  * @return
-  */
+   * Returns a flattened matrix containing the sum of each column
+   *
+   * @return
+   */
   double[] sumCol();
 
   /**
-  * returns the mean value of the complete matrix
-  */
+   * returns the mean value of the complete matrix
+   */
   double mean();
 
   /**
-  * fills up a diagonal matrix with its transposed copy
-  * !other side should be filled with either 0 or Double.NaN
-  */
+   * fills up a diagonal matrix with its transposed copy !other side should be
+   * filled with either 0 or Double.NaN
+   */
   void fillDiagonal();
 
   /**
-  * counts the number of Double.NaN in the matrix
-  *
-  * @return
-  */
+   * counts the number of Double.NaN in the matrix
+   *
+   * @return
+   */
   int countNaN();
 
   /**
-  * performs an element-wise addition of this matrix by another matrix
-  * !matrices have to be the same size
-  * @param m ~ other matrix
-  * 
-  * @return
-  * @throws IllegalArgumentException
-  *           if this and m do not have the same dimensions
-  */
+   * performs an element-wise addition of this matrix by another matrix
+   * !matrices have to be the same size
+   * 
+   * @param m
+   *          ~ other matrix
+   * 
+   * @return
+   * @throws IllegalArgumentException
+   *           if this and m do not have the same dimensions
+   */
   MatrixI add(MatrixI m);
 
   /**
-  * performs an element-wise subtraction of this matrix by another matrix
-  * !matrices have to be the same size
-  * @param m ~ other matrix
-  * 
-  * @return
-  * @throws IllegalArgumentException
-  *           if this and m do not have the same dimensions
-  */
+   * performs an element-wise subtraction of this matrix by another matrix
+   * !matrices have to be the same size
+   * 
+   * @param m
+   *          ~ other matrix
+   * 
+   * @return
+   * @throws IllegalArgumentException
+   *           if this and m do not have the same dimensions
+   */
   MatrixI subtract(MatrixI m);
+
   /**
-  * performs an element-wise multiplication of this matrix by another matrix ~ this * m
-  * !matrices have to be the same size
-  * @param m ~ other matrix
-  *
-  * @return
-  * @throws IllegalArgumentException
-  *    if this and m do not have the same dimensions
-  */
+   * performs an element-wise multiplication of this matrix by another matrix ~
+   * this * m !matrices have to be the same size
+   * 
+   * @param m
+   *          ~ other matrix
+   *
+   * @return
+   * @throws IllegalArgumentException
+   *           if this and m do not have the same dimensions
+   */
   MatrixI elementwiseMultiply(MatrixI m);
 
   /**
-  * performs an element-wise division of this matrix by another matrix ~ this / m
-  * !matrices have to be the same size
-  * @param m ~ other matrix
-  *
-  * @return
-  * @throws IllegalArgumentException
-  *    if this and m do not have the same dimensions
-  */
+   * performs an element-wise division of this matrix by another matrix ~ this /
+   * m !matrices have to be the same size
+   * 
+   * @param m
+   *          ~ other matrix
+   *
+   * @return
+   * @throws IllegalArgumentException
+   *           if this and m do not have the same dimensions
+   */
   MatrixI elementwiseDivide(MatrixI m);
 
   /**
-  * calculates the root-mean-square for two matrices
-  * @param m ~ other matrix
-  *  
-  * @return
-  */
+   * calculates the root-mean-square for two matrices
+   * 
+   * @param m
+   *          ~ other matrix
+   * 
+   * @return
+   */
   double rmsd(MatrixI m);
 
   /**
-  * calculates the Frobenius norm of this matrix
-  *
-  * @return
-  */
+   * calculates the Frobenius norm of this matrix
+   *
+   * @return
+   */
   double norm();
-  
+
   /**
-  * returns the sum of all values in this matrix
-  *
-  * @return
-  */
+   * returns the sum of all values in this matrix
+   *
+   * @return
+   */
   double sum();
 
   /**
-  * returns the sum-product of this matrix with vector v
-  * @param v ~ vector
-  *
-  * @return
-  * @throws IllegalArgumentException
-  *    if this.cols and v do not have the same length
-  */
+   * returns the sum-product of this matrix with vector v
+   * 
+   * @param v
+   *          ~ vector
+   *
+   * @return
+   * @throws IllegalArgumentException
+   *           if this.cols and v do not have the same length
+   */
   double[] sumProduct(double[] v);
 
   /**
-  * mirrors the columns of this matrix
-  *
-  * @return
-  */
+   * mirrors the columns of this matrix
+   *
+   * @return
+   */
   MatrixI mirrorCol();
 }
index b5218d5..eee7ef8 100755 (executable)
@@ -27,14 +27,17 @@ import java.util.Arrays;
 
 /**
  * A collection of miscellaneous mathematical operations
+ * 
  * @AUTHOR MorellThomas
  */
 public class MiscMath
 {
   /**
-  * prints an array
-  * @param m ~ array
-  */
+   * prints an array
+   * 
+   * @param m
+   *          ~ array
+   */
   public static void print(double[] m, String format)
   {
     System.out.print("[ ");
@@ -46,39 +49,43 @@ public class MiscMath
   }
 
   /**
-  * calculates the mean of an array 
-  *
-  * @param m ~ array
-  * @return
-  */
+   * calculates the mean of an array
+   *
+   * @param m
+   *          ~ array
+   * @return
+   */
   public static double mean(double[] m)
   {
     double sum = 0;
     int nanCount = 0;
     for (int i = 0; i < m.length; i++)
     {
-      if (!Double.isNaN(m[i])) // ignore NaN values in the array
+      if (!Double.isNaN(m[i])) // ignore NaN values in the array
       {
         sum += m[i];
-      } else {
-       nanCount++;
+      }
+      else
+      {
+        nanCount++;
       }
     }
     return sum / (double) (m.length - nanCount);
   }
 
   /**
-  * calculates the sum of an array 
-  *
-  * @param m ~ array
-  * @return
-  */
+   * calculates the sum of an array
+   *
+   * @param m
+   *          ~ array
+   * @return
+   */
   public static double sum(double[] m)
   {
     double sum = 0;
     for (int i = 0; i < m.length; i++)
     {
-      if (!Double.isNaN(m[i])) // ignore NaN values in the array
+      if (!Double.isNaN(m[i])) // ignore NaN values in the array
       {
         sum += m[i];
       }
@@ -87,14 +94,14 @@ public class MiscMath
   }
 
   /**
-  * calculates the square root of each element in an array
-  *
-  * @param m ~ array
-  *
-  * @return
-  * TODO
-  * make general with function passed -> apply function to each element
-  */
+   * calculates the square root of each element in an array
+   *
+   * @param m
+   *          ~ array
+   *
+   * @return TODO make general with function passed -> apply function to each
+   *         element
+   */
   public static double[] sqrt(double[] m)
   {
     double[] sqrts = new double[m.length];
@@ -106,16 +113,20 @@ public class MiscMath
   }
 
   /**
-  * calculate element wise multiplication of two arrays with the same length
-  *
-  * @param a ~ array
-  * @param b ~ array
-  *
-  * @return
-  */
-  public static double[] elementwiseMultiply(byte[] a, double[] b) throws RuntimeException
+   * calculate element wise multiplication of two arrays with the same length
+   *
+   * @param a
+   *          ~ array
+   * @param b
+   *          ~ array
+   *
+   * @return
+   */
+  public static double[] elementwiseMultiply(byte[] a, double[] b)
+          throws RuntimeException
   {
-    if (a.length != b.length)  // throw exception if the arrays do not have the same length
+    if (a.length != b.length) // throw exception if the arrays do not have the
+                              // same length
     {
       throw new SameLengthException(a.length, b.length);
     }
@@ -126,9 +137,12 @@ public class MiscMath
     }
     return result;
   }
-  public static double[] elementwiseMultiply(double[] a, double[] b) throws RuntimeException
+
+  public static double[] elementwiseMultiply(double[] a, double[] b)
+          throws RuntimeException
   {
-    if (a.length != b.length)  // throw exception if the arrays do not have the same length
+    if (a.length != b.length) // throw exception if the arrays do not have the
+                              // same length
     {
       throw new SameLengthException(a.length, b.length);
     }
@@ -139,9 +153,12 @@ public class MiscMath
     }
     return result;
   }
-  public static byte[] elementwiseMultiply(byte[] a, byte[] b) throws RuntimeException
+
+  public static byte[] elementwiseMultiply(byte[] a, byte[] b)
+          throws RuntimeException
   {
-    if (a.length != b.length)  // throw exception if the arrays do not have the same length
+    if (a.length != b.length) // throw exception if the arrays do not have the
+                              // same length
     {
       throw new SameLengthException(a.length, b.length);
     }
@@ -152,6 +169,7 @@ public class MiscMath
     }
     return result;
   }
+
   public static double[] elementwiseMultiply(double[] a, double b)
   {
     double[] result = new double[a.length];
@@ -163,16 +181,20 @@ public class MiscMath
   }
 
   /**
-  * calculate element wise division of two arrays ~ a / b
-  *
-  * @param a ~ array
-  * @param b ~ array
-  *
-  * @return
-  */
-  public static double[] elementwiseDivide(double[] a, double[] b) throws RuntimeException
+   * calculate element wise division of two arrays ~ a / b
+   *
+   * @param a
+   *          ~ array
+   * @param b
+   *          ~ array
+   *
+   * @return
+   */
+  public static double[] elementwiseDivide(double[] a, double[] b)
+          throws RuntimeException
   {
-    if (a.length != b.length)  // throw exception if the arrays do not have the same length
+    if (a.length != b.length) // throw exception if the arrays do not have the
+                              // same length
     {
       throw new SameLengthException(a.length, b.length);
     }
@@ -185,16 +207,20 @@ public class MiscMath
   }
 
   /**
-  * calculate element wise addition of two arrays
-  *
-  * @param a ~ array
-  * @param b ~ array
-  *
-  * @return
-  */
-  public static double[] elementwiseAdd(double[] a, double[] b) throws RuntimeException
+   * calculate element wise addition of two arrays
+   *
+   * @param a
+   *          ~ array
+   * @param b
+   *          ~ array
+   *
+   * @return
+   */
+  public static double[] elementwiseAdd(double[] a, double[] b)
+          throws RuntimeException
   {
-    if (a.length != b.length)  // throw exception if the arrays do not have the same length
+    if (a.length != b.length) // throw exception if the arrays do not have the
+                              // same length
     {
       throw new SameLengthException(a.length, b.length);
     }
@@ -206,6 +232,7 @@ public class MiscMath
     }
     return result;
   }
+
   public static double[] elementwiseAdd(double[] a, double b)
   {
     double[] result = new double[a.length];
@@ -217,41 +244,59 @@ public class MiscMath
   }
 
   /**
-  * returns true if two arrays are element wise within a tolerance
-  *
-  * @param a ~ array
-  * @param b ~ array
-  * @param rtol ~ relative tolerance
-  * @param atol ~ absolute tolerance
-  * @param equalNAN ~ whether NaN at the same position return true
-  *
-  * @return
-  */
-  public static boolean allClose(double[] a, double[] b, double rtol, double atol, boolean equalNAN)
+   * returns true if two arrays are element wise within a tolerance
+   *
+   * @param a
+   *          ~ array
+   * @param b
+   *          ~ array
+   * @param rtol
+   *          ~ relative tolerance
+   * @param atol
+   *          ~ absolute tolerance
+   * @param equalNAN
+   *          ~ whether NaN at the same position return true
+   *
+   * @return
+   */
+  public static boolean allClose(double[] a, double[] b, double rtol,
+          double atol, boolean equalNAN)
   {
     boolean areEqual = true;
     for (int i = 0; i < a.length; i++)
     {
-      if (equalNAN && (Double.isNaN(a[i]) && Double.isNaN(b[i])))      // if equalNAN == true -> skip the NaN pair
+      if (equalNAN && (Double.isNaN(a[i]) && Double.isNaN(b[i]))) // if equalNAN
+                                                                  // == true ->
+                                                                  // skip the
+                                                                  // NaN pair
       {
-       continue;
+        continue;
       }
-      if (Math.abs(a[i] - b[i]) > (atol + rtol * Math.abs(b[i])))      // check for the similarity condition -> if not met -> break and return false
+      if (Math.abs(a[i] - b[i]) > (atol + rtol * Math.abs(b[i]))) // check for
+                                                                  // the
+                                                                  // similarity
+                                                                  // condition
+                                                                  // -> if not
+                                                                  // met ->
+                                                                  // break and
+                                                                  // return
+                                                                  // false
       {
-       areEqual = false;
-       break;
+        areEqual = false;
+        break;
       }
     }
     return areEqual;
   }
 
   /**
-  * returns the index of the maximum and the maximum value of an array
-  * 
-  * @param a ~ array
-  *
-  * @return
-  */
+   * returns the index of the maximum and the maximum value of an array
+   * 
+   * @param a
+   *          ~ array
+   *
+   * @return
+   */
   public static int[] findMax(int[] a)
   {
     int max = 0;
@@ -260,25 +305,30 @@ public class MiscMath
     {
       if (a[i] > max)
       {
-       max = a[i];
-       maxIndex = i;
+        max = a[i];
+        maxIndex = i;
       }
     }
-    return new int[]{maxIndex, max};
+    return new int[] { maxIndex, max };
   }
 
   /**
-  * returns the dot product of two arrays
-  * @param a ~ array a
-  * @param b ~ array b
-  *
-  * @return
-  */
+   * returns the dot product of two arrays
+   * 
+   * @param a
+   *          ~ array a
+   * @param b
+   *          ~ array b
+   *
+   * @return
+   */
   public static double dot(double[] a, double[] b)
   {
     if (a.length != b.length)
     {
-      throw new IllegalArgumentException(String.format("Vectors do not have the same length (%d, %d)!", a.length, b.length));
+      throw new IllegalArgumentException(
+              String.format("Vectors do not have the same length (%d, %d)!",
+                      a.length, b.length));
     }
 
     double aibi = 0;
@@ -290,11 +340,13 @@ public class MiscMath
   }
 
   /**
-  * returns the euklidian norm of the vector
-  * @param v ~ vector
-  *
-  * @return
-  */
+   * returns the euklidian norm of the vector
+   * 
+   * @param v
+   *          ~ vector
+   *
+   * @return
+   */
   public static double norm(double[] v)
   {
     double result = 0;
@@ -302,15 +354,17 @@ public class MiscMath
     {
       result += Math.pow(i, 2);
     }
-  return Math.sqrt(result);
+    return Math.sqrt(result);
   }
 
   /**
-  * returns the number of NaN in the vector
-  * @param v ~ vector
-  *
-  * @return
-  */
+   * returns the number of NaN in the vector
+   * 
+   * @param v
+   *          ~ vector
+   *
+   * @return
+   */
   public static int countNaN(double[] v)
   {
     int cnt = 0;
@@ -318,20 +372,21 @@ public class MiscMath
     {
       if (Double.isNaN(i))
       {
-       cnt++;
+        cnt++;
       }
     }
     return cnt;
   }
 
   /**
-  * recursively calculates the permutations of total n items with r items per combination
-  * according to n!/(n-r)! by only multiplying the relevant terms
-  * @param n 
-  * @param r
-  *
-  * @return permutations
-  */
+   * recursively calculates the permutations of total n items with r items per
+   * combination according to n!/(n-r)! by only multiplying the relevant terms
+   * 
+   * @param n
+   * @param r
+   *
+   * @return permutations
+   */
   public static long permutations(int n, int r)
   {
     if (n < r)
@@ -340,13 +395,14 @@ public class MiscMath
     long result = 1l;
     for (int i = 0; i < r; i++)
     {
-      result *= (n-i);
+      result *= (n - i);
     }
     return result;
   }
 
   /**
    * calculate all unique combinations of n elements into r sized groups
+   * 
    * @param n
    * @param r
    *
@@ -357,13 +413,14 @@ public class MiscMath
     int result = 1;
     for (int i = 0; i < r; i++)
     {
-      result *= (n-1);
+      result *= (n - 1);
     }
     return (int) (result / MiscMath.factorial(r));
   }
 
   /**
    * calculate the factorial of n (n >= 0)
+   * 
    * @param n
    *
    * @return
index 2a2225e..90a12f2 100644 (file)
@@ -2011,13 +2011,14 @@ public class Jalview2XML
   }
 
   /**
-   * Writes PaSiMap viewer attributes and computed values to an XML model object and
-   * adds it to the JalviewModel. Any exceptions are reported by logging.
+   * Writes PaSiMap viewer attributes and computed values to an XML model object
+   * and adds it to the JalviewModel. Any exceptions are reported by logging.
    * uses the functions from PCA
    */
   protected void savePaSiMap(PaSiMapPanel panel, JalviewModel object)
   {
-    // TODO: this should be merged with above savePCAPanel - otherwise it is essentially redundant code
+    // TODO: this should be merged with above savePCAPanel - otherwise it is
+    // essentially redundant code
     try
     {
       PcaViewer viewer = new PcaViewer();
@@ -2107,6 +2108,7 @@ public class Jalview2XML
       Console.error("Error saving PaSiMap: " + t.getMessage());
     }
   }
+
   /**
    * Stores values from a matrix into an XML element, including (if present) the
    * D or E vectors
@@ -6751,9 +6753,9 @@ public class Jalview2XML
 
   private boolean isPasimap(PcaViewer viewer)
   {
-    return viewer.getTitle().toLowerCase(Locale.ROOT)
-    .startsWith("pasimap");
+    return viewer.getTitle().toLowerCase(Locale.ROOT).startsWith("pasimap");
   }
+
   /**
    * Loads any saved PaSiMAp viewers using the function from PCA
    * 
@@ -6777,7 +6779,7 @@ public class Jalview2XML
                 viewer.isIncludeGappedColumns(), viewer.isMatchGaps(),
                 viewer.isIncludeGaps(),
                 viewer.isDenominateByShortestLength());
-       */
+        */
 
         /*
          * create the panel (without computing the PaSiMAp)
@@ -6804,7 +6806,8 @@ public class Jalview2XML
         PaSiMap pasimap = new PaSiMap(null, scoreModel, null);
         PcaDataType pasimapData = viewer.getPcaData();
 
-        MatrixI pairwise = loadDoubleMatrix(pasimapData.getPairwiseMatrix());
+        MatrixI pairwise = loadDoubleMatrix(
+                pasimapData.getPairwiseMatrix());
         pasimap.setPairwiseScores(pairwise);
 
         MatrixI result = loadDoubleMatrix(pasimapData.getEigenMatrix());
index 7c9ecd7..b9411c3 100644 (file)
@@ -1560,8 +1560,6 @@ public class AnnotationRenderer
          * {profile type, #values, total count, char1, pct1, char2, pct2...}
          */
         int profl[] = getProfileFor(_aa, column);
-        
-        
 
         // just try to draw the logo if profl is not null
         if (profl != null && profl[2] != 0)
@@ -1648,7 +1646,7 @@ public class AnnotationRenderer
             }
             else
             {
-              
+
               colour = profcolour.findColour(dc[0], column, null);
 
             }
index 91f38c5..511a55c 100644 (file)
@@ -63,13 +63,14 @@ public class ResidueShader implements ResidueShaderI
    * the consensus data for each column
    */
   private ProfilesI consensus;
-  
+
   /*
    * the ss consensus data for each column for each source
    */
   
   private Map<String, ProfilesI> ssConsensusProfileMap;
 
+
   /*
    * if true, apply shading of colour by conservation
    */
@@ -158,7 +159,7 @@ public class ResidueShader implements ResidueShaderI
   public void setConsensus(ProfilesI cons)
   {
     consensus = cons;
-    
+
   }
 
   /**
@@ -303,7 +304,7 @@ public class ResidueShader implements ResidueShaderI
 
     return colour;
   }
-  
+
   @Override
   public Color findSSColour(char symbol, int position)
   {
index 04097a8..8e64f8f 100755 (executable)
@@ -364,62 +364,72 @@ public class ClustalxColourScheme extends ResidueColourScheme
   {
     return false;
   }
+
   public String toRuleRep()
   {
     makeColours();
-    HashMap<String, String> cols=new HashMap();
-    for (String res:ResidueProperties.aa) {
+    HashMap<String, String> cols = new HashMap();
+    for (String res : ResidueProperties.aa)
+    {
       StringBuilder sb = new StringBuilder();
-      
-      int rnum=ResidueProperties.aaIndex[res.charAt(0)];
-      if (rnum<0 || rnum>=residueColour.length)
+
+      int rnum = ResidueProperties.aaIndex[res.charAt(0)];
+      if (rnum < 0 || rnum >= residueColour.length)
       {
         continue;
       }
-      
+
       ConsensusColour cc = residueColour[rnum];
-      if (cc==null)
+      if (cc == null)
       {
         continue;
       }
-      //sb.append("Residue "+res+" ("+rnum+")");
-      //sb.append("\t");
+      // sb.append("Residue "+res+" ("+rnum+")");
+      // sb.append("\t");
       sb.append(cc.c.toString());
-      double f=0;
+      double f = 0;
       sb.append("\t");
-      for (Consensus cons: cc.cons) {
-        if (cons.threshold==0 || f!=cons.threshold)
+      for (Consensus cons : cc.cons)
+      {
+        if (cons.threshold == 0 || f != cons.threshold)
         {
-          if (f!=0)
+          if (f != 0)
           {
 
-              sb.append("}, {");
-          } else {
-          sb.append("{");
+            sb.append("}, {");
+          }
+          else
+          {
+            sb.append("{");
           }
-        sb.append(cons.threshold);
-        sb.append(",");
-        f=cons.threshold;
-        } else {
+          sb.append(cons.threshold);
+          sb.append(",");
+          f = cons.threshold;
+        }
+        else
+        {
           sb.append(",");
         }
         sb.append(cons.maskstr);
       }
       sb.append("}");
-      String clxrep=sb.toString();
+      String clxrep = sb.toString();
       String xres = cols.get(clxrep);
-      if (xres==null) { xres = "";}
-      xres+=res;
+      if (xres == null)
+      {
+        xres = "";
+      }
+      xres += res;
       cols.put(clxrep, xres);
     }
     StringBuilder sb = new StringBuilder();
-    for (String clxrep:cols.keySet())
+    for (String clxrep : cols.keySet())
     {
       sb.append(cols.get(clxrep));
       sb.append("\t");
       sb.append(clxrep);
       sb.append("\n");
-      
+
     }
     return sb.toString();
   }
index 2fcb95f..9d83069 100755 (executable)
@@ -41,7 +41,7 @@ public class ResidueProperties
   public static final int[] nucleotideIndex;
 
   public static final int[] purinepyrimidineIndex;
-  
+
   public static final int[] secondaryStructureIndex;
 
   public static final Map<String, Integer> aa3Hash = new HashMap<>();
@@ -197,13 +197,13 @@ public class ResidueProperties
     purinepyrimidineIndex['N'] = 2;
     purinepyrimidineIndex['n'] = 2;
   }
-  
+
   static
   {
     secondaryStructureIndex = new int[255];
     for (int i = 0; i < 255; i++)
     {
-      secondaryStructureIndex[i] = 3; 
+      secondaryStructureIndex[i] = 3;
     }
 
     secondaryStructureIndex['H'] = 0;
@@ -398,12 +398,12 @@ public class ResidueProperties
       Color.white, // all other nucleotides
       Color.white // Gap
   };
-  
-  //Secondary structure
-   public static final Color[] secondarystructure = { Color.red, // H
-       Color.green, // E
-       Color.gray  // C
-   };
+
+  // Secondary structure
+  public static final Color[] secondarystructure = { Color.red, // H
+      Color.green, // E
+      Color.gray // C
+  };
 
   // Zappo
   public static final Color[] zappo = { Color.pink, // A
index a71b462..710c054 100644 (file)
@@ -1,3 +1,23 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
 package jalview.util;
 
 import java.util.ArrayList;
index 27513ab..f066892 100644 (file)
@@ -28,19 +28,22 @@ import java.util.Map;
  */
 public class Constants
 {
-  
-  //character used to represent secondary structures
+
+  // character used to represent secondary structures
   public static final char HELIX = 'H';
+
   public static final char SHEET = 'E';
+
   public static final char COIL = 'C';
 
-  //label in secondary structure annotation data model from 3d structures
+  // label in secondary structure annotation data model from 3d structures
   public static final String SS_ANNOTATION_LABEL = "Secondary Structure";
-  
-  //label in secondary structure annotation data model from JPred
-  public static final String SS_ANNOTATION_FROM_JPRED_LABEL = "jnetpred";  
-  
+
+  // label in secondary structure annotation data model from JPred
+  public static final String SS_ANNOTATION_FROM_JPRED_LABEL = "jnetpred";
+
   public static final Map<String, String> SECONDARY_STRUCTURE_LABELS = new HashMap<>();
+
   static {
       SECONDARY_STRUCTURE_LABELS.put(SS_ANNOTATION_LABEL, "3D Structures");
       SECONDARY_STRUCTURE_LABELS.put(SS_ANNOTATION_FROM_JPRED_LABEL, "JPred");
index c204783..651073f 100644 (file)
@@ -1,3 +1,23 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
 package jalview.util;
 
 import java.lang.reflect.InvocationTargetException;
@@ -17,11 +37,18 @@ public class ErrorLog
 
   private static String prefix = null;
 
+  private static boolean quiet = false;
+
   public static void setHasConsole(boolean b)
   {
     hasConsole = b;
   }
 
+  public static void setQuiet(boolean b)
+  {
+    quiet = b;
+  }
+
   public static void setPrefix(String s)
   {
     prefix = s;
@@ -45,6 +72,10 @@ public class ErrorLog
   public static void println(String message0, boolean err,
           boolean thisHasConsole)
   {
+    if (!err && quiet)
+    {
+      return;
+    }
     String message = prefix == null ? message0 : prefix + message0;
     if (thisHasConsole && hasConsole)
     {
index ec35948..1d9f8d0 100644 (file)
@@ -30,6 +30,8 @@ import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.URLConnection;
 
+import jalview.bin.Console;
+
 public class HttpUtils
 {
   public final static String JALVIEWSCHEMEPREFIX = "jalview";
@@ -129,7 +131,9 @@ public class HttpUtils
   }
 
   /**
-   * wrapper to follow a URL connection ALLOWING redirects from http to https
+   * wrapper to return a new HttpURLConnection to a new URL when there is a
+   * redirect from http to https, otherwise return the unused original
+   * HttpURLConnection
    * 
    * @param HttpURLConnection
    *          conn0
@@ -139,34 +143,54 @@ public class HttpUtils
           throws IOException
   {
     URL url = conn0.getURL();
-    if (url == null)
+    // we are only checking for a redirect from http to https otherwise the java
+    // connection will follow when called (if not unset)
+    if (url == null || !"http".equals(url.getProtocol())
+            || !conn0.getInstanceFollowRedirects())
     {
-      return null;
+      return conn0;
     }
-    HttpURLConnection conn = null;
-    int response = conn0.getResponseCode();
-    boolean followed = false;
-    if (response >= 300 && response < 400 && conn0.getFollowRedirects())
+
+    // check the response code
+    HttpURLConnection checkConn = (HttpURLConnection) url.openConnection();
+    httpURLConnectionCopyAttributes(conn0, checkConn);
+
+    boolean redirectToHttps = false;
+    int response = checkConn.getResponseCode();
+    checkConn.disconnect();
+    if (response >= 300 && response < 400)
     {
       // we are only checking for a redirect from http to https
-      if ("http".equals(url.getProtocol()))
+      URL loc = new URL(conn0.getHeaderField("Location"));
+      if (loc != null && "https".equals(loc.getProtocol()))
       {
-        URL loc = new URL(conn0.getHeaderField("Location"));
-        if (loc != null && "https".equals(loc.getProtocol()))
-        {
-          conn = (HttpURLConnection) loc.openConnection();
-          conn.setRequestMethod(conn0.getRequestMethod());
-          conn.setDoInput(conn0.getDoInput());
-          conn.setUseCaches(conn0.getUseCaches());
-          conn.setConnectTimeout(conn0.getConnectTimeout());
-          conn.setReadTimeout(conn0.getReadTimeout());
-          conn.setInstanceFollowRedirects(
-                  conn0.getInstanceFollowRedirects());
-          followed = true;
-        }
+        redirectToHttps = true;
+        url = loc;
       }
     }
-    return followed && conn != null ? conn : conn0;
+
+    if (!redirectToHttps)
+    {
+      return conn0;
+    }
+
+    // We want to return an HttpURLConnection to the new https URL that is
+    // unconnected in case further manipulation of the request is required
+    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+    httpURLConnectionCopyAttributes(conn0, conn);
+    return conn;
+  }
+
+  private static void httpURLConnectionCopyAttributes(
+          HttpURLConnection conn0, HttpURLConnection conn1)
+          throws ProtocolException
+  {
+    conn1.setRequestMethod(conn0.getRequestMethod());
+    conn1.setDoInput(conn0.getDoInput());
+    conn1.setUseCaches(conn0.getUseCaches());
+    conn1.setConnectTimeout(conn0.getConnectTimeout());
+    conn1.setReadTimeout(conn0.getReadTimeout());
+    conn1.setInstanceFollowRedirects(conn0.getInstanceFollowRedirects());
   }
 
   /**
@@ -180,8 +204,11 @@ public class HttpUtils
   {
     if (url == null)
     {
+      Console.debug("HttpUtils.openConnection(url) called with null url");
       return null;
     }
+    Console.debug("HttpUtils.openConnection(url) called with url="
+            + url.toString());
     URLConnection conn = null;
     String protocol = url.getProtocol();
     if ("http".equals(protocol) || "https".equals(protocol))
index 186745d..ccf9992 100644 (file)
@@ -621,7 +621,7 @@ public class LaunchUtils
     }
     String v0 = getJarImplementationVersion(f0);
     String v1 = getJarImplementationVersion(f1);
-    syserr(true, false,
+    syserr(v0 != null && !v0.equals(v1), false,
             "Got launcher versions '" + v0 + "' and '" + v1 + "'");
     return compareGetdownLauncherJarVersions(v0, v1);
   }
index b488b00..f943e2e 100644 (file)
@@ -1,3 +1,23 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
 package jalview.util;
 
 import jalview.bin.Cache;
index 94a1424..2d34db5 100644 (file)
@@ -757,7 +757,7 @@ public abstract class AlignmentViewport
   protected AlignmentAnnotation quality;
 
   protected AlignmentAnnotation[] groupConsensus;
-  
+
   protected AlignmentAnnotation[] groupSSConsensus;
 
   protected AlignmentAnnotation[] groupConservation;
@@ -768,8 +768,6 @@ public abstract class AlignmentViewport
   protected ProfilesI hconsensus = null;
   
   protected Map<String, ProfilesI> hSSConsensusProfileMap = null;
-  
-  
 
   /**
    * results of cDNA complement consensus visible portion of view
@@ -833,13 +831,13 @@ public abstract class AlignmentViewport
   {
     this.hconsensus = hconsensus;
   }
-  
+
   @Override
   public void setSequenceSSConsensusHash(Map<String, ProfilesI> hSSConsensusProfileMap)
   {
     this.hSSConsensusProfileMap = hSSConsensusProfileMap;
   }
-    
+
   @Override
   public void setComplementConsensusHash(
           Hashtable<String, Object>[] hconsensus)
@@ -898,13 +896,11 @@ public abstract class AlignmentViewport
     return consensus;
   }
 
-  
   @Override
   public List<AlignmentAnnotation> getAlignmentSecondaryStructureConsensusAnnotation()
   {
     return secondaryStructureConsensus;
   }
-  
 
   @Override
   public AlignmentAnnotation getAlignmentGapAnnotation()
@@ -997,9 +993,6 @@ public abstract class AlignmentViewport
       }
     }
   }
-  
-  
-  
 
   /**
    * trigger update of Secondary Structure consensus annotation
@@ -1043,7 +1036,8 @@ public abstract class AlignmentViewport
     if (calculator
             .getRegisteredWorkersOfClass(SecondaryStructureConsensusThread.class) == null)
     {
-      calculator.registerWorker(new SecondaryStructureConsensusThread(this, ap));
+      calculator.registerWorker(
+              new SecondaryStructureConsensusThread(this, ap));
     }
     ap.adjustAnnotationHeight();
 
@@ -1162,7 +1156,7 @@ public abstract class AlignmentViewport
    * should consensus profile be rendered by default
    */
   protected boolean showSequenceLogo = false;
-  
+
   /**
    * should consensus profile be rendered normalised to row height
    */
@@ -1172,7 +1166,7 @@ public abstract class AlignmentViewport
    * should consensus histograms be rendered by default
    */
   protected boolean showConsensusHistogram = true;
-  
+
   /**
    * @return the showConsensusProfile
    */
@@ -1181,7 +1175,7 @@ public abstract class AlignmentViewport
   {
     return showSequenceLogo;
   }
-  
+
   /**
    * @param showSequenceLogo
    *          the new value
@@ -1203,7 +1197,7 @@ public abstract class AlignmentViewport
     }
     this.showSequenceLogo = showSequenceLogo;
   }
-  
+
   /**
    * @param showConsensusHistogram
    *          the showConsensusHistogram to set
@@ -1501,7 +1495,7 @@ public abstract class AlignmentViewport
 
   protected boolean showQuality = true;
 
-  protected boolean showConsensus = true;  
+  protected boolean showConsensus = true;
 
   protected boolean showSSConsensus = true;
 
@@ -2169,7 +2163,6 @@ public abstract class AlignmentViewport
     }
   }
 
-
   /**
    * If this is a protein alignment and there are mappings to cDNA, adds the
    * cDNA consensus annotation and returns true, else returns false.
index a0e5174..8568af4 100644 (file)
@@ -69,8 +69,8 @@ public class PaSiMapModel
    * @param modelName
    * @param params
    */
-  public PaSiMapModel(AlignmentViewport seqData, SequenceI[] sqs, boolean nuc,
-          ScoreModelI modelName)
+  public PaSiMapModel(AlignmentViewport seqData, SequenceI[] sqs,
+          boolean nuc, ScoreModelI modelName)
   {
     inputData = seqData;
     seqs = sqs;
@@ -79,8 +79,8 @@ public class PaSiMapModel
   }
 
   /**
-   * Performs the PaSiMap calculation (in the same thread) and extracts result data
-   * needed for visualisation by PaSiMapPanel
+   * Performs the PaSiMap calculation (in the same thread) and extracts result
+   * data needed for visualisation by PaSiMapPanel
    */
   public void calculate(PairwiseAlignPanel pap)
   {
@@ -100,7 +100,8 @@ public class PaSiMapModel
     top = width;
 
     points = new Vector<>();
-    Point[] scores = pasimap.getComponents(width - 1, width - 2, width - 3, 1);
+    Point[] scores = pasimap.getComponents(width - 1, width - 2, width - 3,
+            1);
 
     for (int i = 0; i < height; i++)
     {
@@ -142,8 +143,9 @@ public class PaSiMapModel
   /**
    * Updates the 3D coordinates for the list of points to the given dimensions.
    * Principal dimension is getTop(). Next greatest eigenvector is getTop()-1.
-   * Note - pasimap.getComponents starts counting the spectrum from rank-2 to zero,
-   * rather than rank-1, so getComponents(dimN ...) == updateRcView(dimN+1 ..)
+   * Note - pasimap.getComponents starts counting the spectrum from rank-2 to
+   * zero, rather than rank-1, so getComponents(dimN ...) == updateRcView(dimN+1
+   * ..)
    * 
    * @param dim1
    * @param dim2
@@ -207,13 +209,15 @@ public class PaSiMapModel
       csv.append("\"" + seqs[s].getName() + "\"");
       if (!transformed)
       {
-       double[] fl = pasimap.component(s);
-       for (int d = fl.length - 1; d >= 0; d--)
-       {
-         csv.append(",");
-         csv.append(fl[d]);
-       }
-      } else {
+        double[] fl = pasimap.component(s);
+        for (int d = fl.length - 1; d >= 0; d--)
+        {
+          csv.append(",");
+          csv.append(fl[d]);
+        }
+      }
+      else
+      {
         Point p = points.get(s).coord;
         csv.append(",").append(p.x);
         csv.append(",").append(p.y);
index 6629f27..259d96b 100644 (file)
@@ -186,7 +186,8 @@ public class SecondaryStructureConsensusThread extends AlignCalcWorker
    */
   protected List<AlignmentAnnotation> getSSConsensusAnnotation()
   {
-    return alignViewport.getAlignmentSecondaryStructureConsensusAnnotation();
+    return alignViewport
+            .getAlignmentSecondaryStructureConsensusAnnotation();
   }
   
   /**
index c299a4d..1ead049 100644 (file)
@@ -1,3 +1,23 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
 package jalview.ws.datamodel.alphafold;
 
 import java.awt.Color;
index 397a84b..017a20f 100644 (file)
@@ -1,3 +1,23 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
 package jalview.ws.datamodel.alphafold;
 
 import java.io.File;
index 03bdd0b..1f94601 100644 (file)
@@ -76,16 +76,18 @@ public class AlignSeqTest
     String s = "aArRnNzZxX *.-?";
     assertArrayEquals(expected, as.indexEncode(s));
   }
-  @Test(groups= {"Functional"})
+
+  @Test(groups = { "Functional" })
   public void testGlobalAlignment()
   {
-    String seq1="CAGCTAGCG",seq2="CCATACGA";
-    Sequence sq1=new Sequence("s1",seq1),sq2=new Sequence("s2",seq2);
+    String seq1 = "CAGCTAGCG", seq2 = "CCATACGA";
+    Sequence sq1 = new Sequence("s1", seq1), sq2 = new Sequence("s2", seq2);
     // AlignSeq doesn't report the unaligned regions at either end of sequences
-    //String alseq1="-CAGCTAGCG-",alseq2="CCA--TA-CGA";
+    // String alseq1="-CAGCTAGCG-",alseq2="CCA--TA-CGA";
     // so we check we have the aligned segment correct only
-    String alseq1="CAGCTAGCG",alseq2="CA--TA-CG";
-    AlignSeq as = AlignSeq.doGlobalNWAlignment(sq1,sq2,AlignSeq.DNA);
-    assertEquals(as.getAStr1()+"\n"+as.getAStr2(),alseq1+"\n"+alseq2);
+    String alseq1 = "CAGCTAGCG", alseq2 = "CA--TA-CG";
+    AlignSeq as = AlignSeq.doGlobalNWAlignment(sq1, sq2, AlignSeq.DNA);
+    assertEquals(as.getAStr1() + "\n" + as.getAStr2(),
+            alseq1 + "\n" + alseq2);
   }
 }
index 1cf9a80..863356e 100644 (file)
@@ -83,25 +83,29 @@ public class AlignmentUtilsTests
   {
     JvOptionPane.setInteractiveMode(false);
     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
-    
-    AlignmentAnnotation ann1 = new AlignmentAnnotation("Secondary Structure", "Secondary Structure",
-            new Annotation[] {});
-    AlignmentAnnotation ann2 = new AlignmentAnnotation("jnetpred", "jnetpred",
+
+    AlignmentAnnotation ann1 = new AlignmentAnnotation(
+            "Secondary Structure", "Secondary Structure",
             new Annotation[] {});
+    AlignmentAnnotation ann2 = new AlignmentAnnotation("jnetpred",
+            "jnetpred", new Annotation[] {});
     AlignmentAnnotation ann3 = new AlignmentAnnotation("Temp", "Temp",
             new Annotation[] {});
     AlignmentAnnotation ann4 = new AlignmentAnnotation("Temp", "Temp",
             new Annotation[] {});
-    
-    AlignmentAnnotation[] anns1 = new AlignmentAnnotation[] {ann1, ann3, ann4};
 
-    AlignmentAnnotation[] anns2 = new AlignmentAnnotation[] {ann2, ann3, ann4};
-    
-    AlignmentAnnotation[] anns3 = new AlignmentAnnotation[] {ann3, ann4};
-    
+    AlignmentAnnotation[] anns1 = new AlignmentAnnotation[] { ann1, ann3,
+        ann4 };
+
+    AlignmentAnnotation[] anns2 = new AlignmentAnnotation[] { ann2, ann3,
+        ann4 };
+
+    AlignmentAnnotation[] anns3 = new AlignmentAnnotation[] { ann3, ann4 };
+
     AlignmentAnnotation[] anns4 = new AlignmentAnnotation[0];
-    
-    AlignmentAnnotation[] anns5 = new AlignmentAnnotation[] {ann1, ann2, ann3, ann4};
+
+    AlignmentAnnotation[] anns5 = new AlignmentAnnotation[] { ann1, ann2,
+        ann3, ann4 };
   }
 
   @Test(groups = { "Functional" })
@@ -2781,133 +2785,180 @@ public class AlignmentUtilsTests
         Assert.assertEquals(expectedSSPresent, AlignmentUtils.isSecondaryStructurePresent(annotations));
     }
 
-    @DataProvider(name = "SecondaryStructureAnnotations")
-    public static Object[][] provideSecondaryStructureAnnotations() {
-        AlignmentAnnotation ann1 = new AlignmentAnnotation("Secondary Structure", "Secondary Structure", new Annotation[]{});
-        AlignmentAnnotation ann2 = new AlignmentAnnotation("jnetpred", "jnetpred", new Annotation[]{});
-        AlignmentAnnotation ann3 = new AlignmentAnnotation("Temp", "Temp", new Annotation[]{});
-        AlignmentAnnotation ann4 = new AlignmentAnnotation("Temp", "Temp", new Annotation[]{});
-
-        List<String> ssSources1 = new ArrayList<>(Arrays.asList("3D Structures"));
-        List<String> ssSources2 = new ArrayList<>(Arrays.asList("JPred"));
-        List<String> ssSources3 = new ArrayList<>(Arrays.asList("3D Structures", "JPred"));
-        List<String> ssSources4 = new ArrayList<>();
-
-        return new Object[][]{
-            {new AlignmentAnnotation[]{ann1, ann3, ann4}, true, ssSources1},
-            {new AlignmentAnnotation[]{ann2, ann3, ann4}, true, ssSources2},
-            {new AlignmentAnnotation[]{ann3, ann4}, false, ssSources4},
-            {new AlignmentAnnotation[]{}, false, ssSources4},
-            {new AlignmentAnnotation[]{ann1, ann2, ann3, ann4}, true, ssSources3}
-        };
-    }
-    
-    @Test(dataProvider = "SecondaryStructureAnnotationColours")
-    public void testSecondaryStructureAnnotationColour(char symbol, Color expectedColor) {
-        Color actualColor = AlignmentUtils.getSecondaryStructureAnnotationColour(symbol);
-        Assert.assertEquals(actualColor, expectedColor);
-    }
+  @Test(
+    groups = "Functional",
+    dataProvider = "SecondaryStructureAnnotations")
+  public void testSecondaryStructurePresentAndSources(
+          AlignmentAnnotation[] annotations, boolean expectedSSPresent,
+          ArrayList<String> expectedSSSources)
+  {
+    Assert.assertEquals(expectedSSPresent,
+            AlignmentUtils.isSecondaryStructurePresent(annotations));
+    Assert.assertEquals(expectedSSSources,
+            AlignmentUtils.getSecondaryStructureSources(annotations));
+  }
 
-    @DataProvider(name = "SecondaryStructureAnnotationColours")
-    public static Object[][] provideSecondaryStructureAnnotationColours() {
-        return new Object[][]{
-            {'C', Color.gray},
-            {'E', Color.green},
-            {'H', Color.red},
-            {'-', Color.gray}
-        };
-    }
-    
-    @Test(dataProvider = "SSAnnotationPresence")
-    public void testIsSSAnnotationPresent(Map<SequenceI, List<AlignmentAnnotation>> annotations, boolean expectedPresence) {
-        boolean actualPresence = AlignmentUtils.isSSAnnotationPresent(annotations);
-        Assert.assertEquals(actualPresence, expectedPresence);
-    }
+  @DataProvider(name = "SecondaryStructureAnnotations")
+  public static Object[][] provideSecondaryStructureAnnotations()
+  {
+    AlignmentAnnotation ann1 = new AlignmentAnnotation(
+            "Secondary Structure", "Secondary Structure",
+            new Annotation[] {});
+    AlignmentAnnotation ann2 = new AlignmentAnnotation("jnetpred",
+            "jnetpred", new Annotation[] {});
+    AlignmentAnnotation ann3 = new AlignmentAnnotation("Temp", "Temp",
+            new Annotation[] {});
+    AlignmentAnnotation ann4 = new AlignmentAnnotation("Temp", "Temp",
+            new Annotation[] {});
 
-    @DataProvider(name = "SSAnnotationPresence")
-    public static Object[][] provideSSAnnotationPresence() {
-        Map<SequenceI, List<AlignmentAnnotation>> annotations1 = new HashMap<>();
-        SequenceI seq1 = new Sequence("Seq1", "ASD---ASD---ASD", 37, 45);
-        List<AlignmentAnnotation> annotationsList1 = new ArrayList<>();
-        annotationsList1.add(new AlignmentAnnotation("Secondary Structure", "Secondary Structure", new Annotation[]{}));
-        annotations1.put(seq1, annotationsList1); // Annotation present secondary structure for seq1
-
-        Map<SequenceI, List<AlignmentAnnotation>> annotations2 = new HashMap<>();
-        SequenceI seq2 = new Sequence("Seq2", "ASD---ASD------", 37, 42);
-        List<AlignmentAnnotation> annotationsList2 = new ArrayList<>();
-        annotationsList2.add(new AlignmentAnnotation("Other Annotation", "Other Annotation", new Annotation[]{}));
-        annotations2.put(seq2, annotationsList2); // Annotation not related to any of secondary structure for seq2
-
-        Map<SequenceI, List<AlignmentAnnotation>> annotations3 = new HashMap<>();
-        // Empty annotation map
-        
-        Map<SequenceI, List<AlignmentAnnotation>> annotations4 = new HashMap<>();
-        SequenceI seq4 = new Sequence("Seq4", "ASD---ASD---AS-", 37, 44);
-        List<AlignmentAnnotation> annotationsList4 = new ArrayList<>();
-        annotationsList4.add(new AlignmentAnnotation("jnetpred", "jnetpred", new Annotation[]{}));
-        annotations4.put(seq4, annotationsList4); // Annotation present from JPred for seq4
-
-
-        return new Object[][]{
-            {annotations1, true}, // Annotations present secondary structure present
-            {annotations2, false}, // No annotations related to any of the secondary structure present
-            {annotations3, false},  // Empty annotation map
-            {annotations4, true}, // Annotations present from JPred secondary structure present
-        };
-    }
-    
-    @Test
-    public void testGetSSSourceFromAnnotationDescription(AlignmentAnnotation[] annotations, String expectedSSSource) {
-        List<String> actualSSSource = AlignmentUtils.extractSSSourceInAlignmentAnnotation(annotations);
-        Assert.assertEquals(actualSSSource, expectedSSSource);
-    }
-    
-    @DataProvider(name = "SSSourceFromAnnotationDescription")
-    public static Object[][] provideSSSourceFromAnnotationDescription() {
-        Map<SequenceI, List<AlignmentAnnotation>> annotations1 = new HashMap<>();
-        SequenceI seq1 = new Sequence("Seq1", "ASD---ASD---ASD", 37, 45);
-        List<AlignmentAnnotation> annotationsList1 = new ArrayList<>();
-        annotationsList1.add(new AlignmentAnnotation("jnetpred", "JPred Output", new Annotation[]{}));
-        annotations1.put(seq1, annotationsList1); // Annotation present from JPred for seq1
-
-        Map<SequenceI, List<AlignmentAnnotation>> annotations2 = new HashMap<>();
-        SequenceI seq2 = new Sequence("Seq2", "ASD---ASD------", 37, 42);
-        List<AlignmentAnnotation> annotationsList2 = new ArrayList<>();
-        annotationsList2.add(new AlignmentAnnotation("Secondary Structure", 
-                "Secondary Structure for af-q43517-f1A", new Annotation[]{}));
-        annotations2.put(seq2, annotationsList2); // Annotation present secondary structure from Alphafold for seq2
-
-        Map<SequenceI, List<AlignmentAnnotation>> annotations3 = new HashMap<>();
-        // Empty annotation map
-        
-        Map<SequenceI, List<AlignmentAnnotation>> annotations4 = new HashMap<>();
-        SequenceI seq4 = new Sequence("Seq4", "ASD---ASD---AS-", 37, 44);
-        List<AlignmentAnnotation> annotationsList4 = new ArrayList<>();
-        annotationsList4.add(new AlignmentAnnotation("Secondary Structure", 
-                "Secondary Structure for 4zhpA", new Annotation[]{}));
-        annotations4.put(seq4, annotationsList4); // Annotation present secondary structure from pdb for seq4
-        
-        Map<SequenceI, List<AlignmentAnnotation>> annotations5 = new HashMap<>();
-        SequenceI seq5 = new Sequence("Seq5", "ASD---ASD---AS-", 37, 44);
-        List<AlignmentAnnotation> annotationsList5 = new ArrayList<>();
-        annotationsList5.add(new AlignmentAnnotation("Secondary Structure", 
-                "Secondary Structure for p09911_54-147__3a7wzn.1.p3502557454997462030P", 
-                new Annotation[]{}));
-        annotations5.put(seq5, annotationsList5); // Annotation present secondary structure from Swiss model for seq5
-        
-
-        //JPred Output - JPred
-        //Secondary Structure for af-q43517-f1A - Alphafold
-        //Secondary Structure for 4zhpA - Experimental
-        //Secondary Structure for p09911_54-147__3a7wzn.1.p3502557454997462030P - Swiss Model
-        
-        return new Object[][]{
-            {annotations1, "JPred"}, 
-            {annotations2, "Alphafold"}, 
-            {annotations3, null},  
-            {annotations4, "PDB"},
-            {annotations5, "Swiss Model"}
-        };
-    }   
+    List<String> ssSources1 = new ArrayList<>(
+            Arrays.asList("3D Structures"));
+    List<String> ssSources2 = new ArrayList<>(Arrays.asList("JPred"));
+    List<String> ssSources3 = new ArrayList<>(
+            Arrays.asList("3D Structures", "JPred"));
+    List<String> ssSources4 = new ArrayList<>();
+
+    return new Object[][] {
+        { new AlignmentAnnotation[]
+        { ann1, ann3, ann4 }, true, ssSources1 },
+        { new AlignmentAnnotation[]
+        { ann2, ann3, ann4 }, true, ssSources2 },
+        { new AlignmentAnnotation[]
+        { ann3, ann4 }, false, ssSources4 },
+        { new AlignmentAnnotation[] {}, false, ssSources4 },
+        { new AlignmentAnnotation[]
+        { ann1, ann2, ann3, ann4 }, true, ssSources3 } };
+  }
+
+  @Test(dataProvider = "SecondaryStructureAnnotationColours")
+  public void testSecondaryStructureAnnotationColour(char symbol,
+          Color expectedColor)
+  {
+    Color actualColor = AlignmentUtils
+            .getSecondaryStructureAnnotationColour(symbol);
+    Assert.assertEquals(actualColor, expectedColor);
+  }
+
+  @DataProvider(name = "SecondaryStructureAnnotationColours")
+  public static Object[][] provideSecondaryStructureAnnotationColours()
+  {
+    return new Object[][] { { 'C', Color.gray }, { 'E', Color.green },
+        { 'H', Color.red },
+        { '-', Color.gray } };
+  }
+
+  @Test(dataProvider = "SSAnnotationPresence")
+  public void testIsSSAnnotationPresent(
+          Map<SequenceI, List<AlignmentAnnotation>> annotations,
+          boolean expectedPresence)
+  {
+    boolean actualPresence = AlignmentUtils
+            .isSSAnnotationPresent(annotations);
+    Assert.assertEquals(actualPresence, expectedPresence);
+  }
+
+  @DataProvider(name = "SSAnnotationPresence")
+  public static Object[][] provideSSAnnotationPresence()
+  {
+    Map<SequenceI, List<AlignmentAnnotation>> annotations1 = new HashMap<>();
+    SequenceI seq1 = new Sequence("Seq1", "ASD---ASD---ASD", 37, 45);
+    List<AlignmentAnnotation> annotationsList1 = new ArrayList<>();
+    annotationsList1.add(new AlignmentAnnotation("Secondary Structure",
+            "Secondary Structure", new Annotation[] {}));
+    annotations1.put(seq1, annotationsList1); // Annotation present secondary
+                                              // structure for seq1
+
+    Map<SequenceI, List<AlignmentAnnotation>> annotations2 = new HashMap<>();
+    SequenceI seq2 = new Sequence("Seq2", "ASD---ASD------", 37, 42);
+    List<AlignmentAnnotation> annotationsList2 = new ArrayList<>();
+    annotationsList2.add(new AlignmentAnnotation("Other Annotation",
+            "Other Annotation", new Annotation[] {}));
+    annotations2.put(seq2, annotationsList2); // Annotation not related to any
+                                              // of secondary structure for seq2
+
+    Map<SequenceI, List<AlignmentAnnotation>> annotations3 = new HashMap<>();
+    // Empty annotation map
+
+    Map<SequenceI, List<AlignmentAnnotation>> annotations4 = new HashMap<>();
+    SequenceI seq4 = new Sequence("Seq4", "ASD---ASD---AS-", 37, 44);
+    List<AlignmentAnnotation> annotationsList4 = new ArrayList<>();
+    annotationsList4.add(new AlignmentAnnotation("jnetpred", "jnetpred",
+            new Annotation[] {}));
+    annotations4.put(seq4, annotationsList4); // Annotation present from JPred
+                                              // for seq4
+
+    return new Object[][] { { annotations1, true }, // Annotations present
+                                                    // secondary structure
+                                                    // present
+        { annotations2, false }, // No annotations related to any of the
+                                 // secondary structure present
+        { annotations3, false }, // Empty annotation map
+        { annotations4, true }, // Annotations present from JPred secondary
+                                // structure present
+    };
+  }
+
+  @Test
+  public void testGetSSSourceFromAnnotationDescription(
+          AlignmentAnnotation[] annotations, String expectedSSSource)
+  {
+    List<String> actualSSSource = AlignmentUtils
+            .extractSSSourceInAlignmentAnnotation(annotations);
+    Assert.assertEquals(actualSSSource, expectedSSSource);
+  }
+
+  @DataProvider(name = "SSSourceFromAnnotationDescription")
+  public static Object[][] provideSSSourceFromAnnotationDescription()
+  {
+    Map<SequenceI, List<AlignmentAnnotation>> annotations1 = new HashMap<>();
+    SequenceI seq1 = new Sequence("Seq1", "ASD---ASD---ASD", 37, 45);
+    List<AlignmentAnnotation> annotationsList1 = new ArrayList<>();
+    annotationsList1.add(new AlignmentAnnotation("jnetpred", "JPred Output",
+            new Annotation[] {}));
+    annotations1.put(seq1, annotationsList1); // Annotation present from JPred
+                                              // for seq1
+
+    Map<SequenceI, List<AlignmentAnnotation>> annotations2 = new HashMap<>();
+    SequenceI seq2 = new Sequence("Seq2", "ASD---ASD------", 37, 42);
+    List<AlignmentAnnotation> annotationsList2 = new ArrayList<>();
+    annotationsList2.add(new AlignmentAnnotation("Secondary Structure",
+            "Secondary Structure for af-q43517-f1A", new Annotation[] {}));
+    annotations2.put(seq2, annotationsList2); // Annotation present secondary
+                                              // structure from Alphafold for
+                                              // seq2
+
+    Map<SequenceI, List<AlignmentAnnotation>> annotations3 = new HashMap<>();
+    // Empty annotation map
+
+    Map<SequenceI, List<AlignmentAnnotation>> annotations4 = new HashMap<>();
+    SequenceI seq4 = new Sequence("Seq4", "ASD---ASD---AS-", 37, 44);
+    List<AlignmentAnnotation> annotationsList4 = new ArrayList<>();
+    annotationsList4.add(new AlignmentAnnotation("Secondary Structure",
+            "Secondary Structure for 4zhpA", new Annotation[] {}));
+    annotations4.put(seq4, annotationsList4); // Annotation present secondary
+                                              // structure from pdb for seq4
+
+    Map<SequenceI, List<AlignmentAnnotation>> annotations5 = new HashMap<>();
+    SequenceI seq5 = new Sequence("Seq5", "ASD---ASD---AS-", 37, 44);
+    List<AlignmentAnnotation> annotationsList5 = new ArrayList<>();
+    annotationsList5.add(new AlignmentAnnotation("Secondary Structure",
+            "Secondary Structure for p09911_54-147__3a7wzn.1.p3502557454997462030P",
+            new Annotation[] {}));
+    annotations5.put(seq5, annotationsList5); // Annotation present secondary
+                                              // structure from Swiss model for
+                                              // seq5
+
+    // JPred Output - JPred
+    // Secondary Structure for af-q43517-f1A - Alphafold
+    // Secondary Structure for 4zhpA - Experimental
+    // Secondary Structure for p09911_54-147__3a7wzn.1.p3502557454997462030P -
+    // Swiss Model
+
+    return new Object[][] { { annotations1, "JPred" },
+        { annotations2, "Alphafold" },
+        { annotations3, null },
+        { annotations4, "PDB" },
+        { annotations5, "Swiss Model" } };
+  }
 
 }
index d0a8047..f82a18a 100644 (file)
@@ -83,7 +83,7 @@ public class ScoreModelsTest
     assertFalse(sm instanceof PairwiseScoreModelI);
     assertTrue(sm instanceof DistanceScoreModel);
     assertEquals(sm.getName(), "Sequence Feature Similarity");
-    
+
     sm = models.next();
     assertFalse(sm instanceof SimilarityScoreModel);
     assertFalse(sm instanceof PairwiseScoreModelI);
index f32be37..03dcdc7 100644 (file)
@@ -48,7 +48,7 @@ import org.testng.annotations.Test;
 // This class tests methods in Class SecondaryStructureDistanceModel 
 public class SecondaryStructureDistanceModelTest
 {
-  
+
   /**
    * Verify computed distances of sequences with gap
    */
@@ -62,7 +62,7 @@ public class SecondaryStructureDistanceModelTest
     ScoreModelI sm = new SecondaryStructureDistanceModel();
     sm = ScoreModels.getInstance().getScoreModel(sm.getName(),
             af.alignPanel);
-    
+
     /*
      * feature distance model always normalises by region width
      * gap-gap is always included (but scores zero)
@@ -73,27 +73,28 @@ public class SecondaryStructureDistanceModelTest
      * include gaps
      * score = 0 + 0 + 1 + 0 = 1/4
      */
-    SimilarityParamsI params = new SimilarityParams(false, true, true, true);
+    SimilarityParamsI params = new SimilarityParams(false, true, true,
+            true);
     params.setSecondaryStructureSource("3D Structures");
     MatrixI distances = sm.findDistances(view, params);
     assertEquals(distances.getValue(0, 0), 1d);
     assertEquals(distances.getValue(1, 1), 1d);
-    assertEquals(distances.getValue(0, 1), 0d); 
+    assertEquals(distances.getValue(0, 1), 0d);
     assertEquals(distances.getValue(1, 0), 0d);
-    
+
     /*
      * exclude gaps
      * score = 0 + 0 + 0 + 0 = 0/4
      */
-    
-    SimilarityParamsI params2 = new SimilarityParams(false, true, false, true);
+
+    SimilarityParamsI params2 = new SimilarityParams(false, true, false,
+            true);
     params2.setSecondaryStructureSource("3D Structures");
     MatrixI distances2 = sm.findDistances(view, params2);
-    assertEquals(distances2.getValue(0, 1), 0d); 
+    assertEquals(distances2.getValue(0, 1), 0d);
     assertEquals(distances2.getValue(1, 0), 0d);
   }
-  
-  
+
   /**
    * Verify computed distances of sequences with gap
    */
@@ -107,7 +108,7 @@ public class SecondaryStructureDistanceModelTest
     ScoreModelI sm = new SecondaryStructureDistanceModel();
     sm = ScoreModels.getInstance().getScoreModel(sm.getName(),
             af.alignPanel);
-    
+
     /*
      * feature distance model always normalises by region width
      * gap-gap is always included (but scores zero)
@@ -118,27 +119,28 @@ public class SecondaryStructureDistanceModelTest
      * include gaps
      * score = 0 + 0 + 2 + 2 = 2/4
      */
-    SimilarityParamsI params = new SimilarityParams(false, true, true, true);
+    SimilarityParamsI params = new SimilarityParams(false, true, true,
+            true);
     params.setSecondaryStructureSource("3D Structures");
     MatrixI distances = sm.findDistances(view, params);
     assertEquals(distances.getValue(0, 0), 1d);
     assertEquals(distances.getValue(1, 1), 1d);
-    assertEquals(distances.getValue(0, 1), 0d); 
+    assertEquals(distances.getValue(0, 1), 0d);
     assertEquals(distances.getValue(1, 0), 0d);
-    
+
     /*
      * exclude gaps
      * score = 0 + 0 + 2 + 2 = 2/4
      */
-    
-    SimilarityParamsI params2 = new SimilarityParams(false, true, false, true);
+
+    SimilarityParamsI params2 = new SimilarityParams(false, true, false,
+            true);
     params2.setSecondaryStructureSource("3D Structures");
     MatrixI distances2 = sm.findDistances(view, params2);
-    assertEquals(distances2.getValue(0, 1), 0d); 
+    assertEquals(distances2.getValue(0, 1), 0d);
     assertEquals(distances2.getValue(1, 0), 0d);
   }
 
-  
   /**
    * Verify computed distances of sequences with gap
    */
@@ -152,7 +154,7 @@ public class SecondaryStructureDistanceModelTest
     ScoreModelI sm = new SecondaryStructureDistanceModel();
     sm = ScoreModels.getInstance().getScoreModel(sm.getName(),
             af.alignPanel);
-    
+
     /*
      * feature distance model always normalises by region width
      * gap-gap is always included (but scores zero)
@@ -163,28 +165,28 @@ public class SecondaryStructureDistanceModelTest
      * include gaps
      * score = 0 + 0 + 2 + 2 = 2/4
      */
-    SimilarityParamsI params = new SimilarityParams(false, true, true, true);
+    SimilarityParamsI params = new SimilarityParams(false, true, true,
+            true);
     params.setSecondaryStructureSource("3D Structures");
     MatrixI distances = sm.findDistances(view, params);
     assertEquals(distances.getValue(0, 0), 1d);
     assertEquals(distances.getValue(1, 1), 1d);
-    assertEquals(distances.getValue(0, 1), 0d); 
+    assertEquals(distances.getValue(0, 1), 0d);
     assertEquals(distances.getValue(1, 0), 0d);
-    
+
     /*
      * exclude gaps
      * score = 0 + 0 + 2 + 2 = 2/4
      */
-    
-    SimilarityParamsI params2 = new SimilarityParams(false, true, false, true);
+
+    SimilarityParamsI params2 = new SimilarityParams(false, true, false,
+            true);
     params2.setSecondaryStructureSource("3D Structures");
     MatrixI distances2 = sm.findDistances(view, params2);
-    assertEquals(distances2.getValue(0, 1), 0d); 
+    assertEquals(distances2.getValue(0, 1), 0d);
     assertEquals(distances2.getValue(1, 0), 0d);
   }
 
-
-  
   /**
    * <pre>
    * Set up
@@ -214,18 +216,17 @@ public class SecondaryStructureDistanceModelTest
             new SequenceFeature("metal", null, 1, 4, 0f, null));
     s2.addSequenceFeature(
             new SequenceFeature("Pfam", null, 1, 4, 0f, null));
-    
-    
+
     /*
      * Set up secondary structure annotations
      */
-    Annotation ssE = new Annotation("","",'E',0);
-    Annotation ssH = new Annotation("","",'H',0);
-    Annotation ssC = new Annotation(".","",' ',0);
-    
+    Annotation ssE = new Annotation("", "", 'E', 0);
+    Annotation ssH = new Annotation("", "", 'H', 0);
+    Annotation ssC = new Annotation(".", "", ' ', 0);
+
     Annotation[] anns1;
     Annotation[] anns2;
-    
+
     /* All secondary structure annotations are similar for each column
      * Set up
     *   column      1 2 3 4 
@@ -235,13 +236,14 @@ public class SecondaryStructureDistanceModelTest
     *        seq s2 F S J L
     *           ss E H S E
     */
-    if(similar == "All Similar") {
-    
-       anns1 = new Annotation[] { ssE, ssH, ssC, ssE};
-       anns2 = new Annotation[] { ssE, ssH, ssC, ssE};
-    
+    if (similar == "All Similar")
+    {
+
+      anns1 = new Annotation[] { ssE, ssH, ssC, ssE };
+      anns2 = new Annotation[] { ssE, ssH, ssC, ssE };
+
     }
-    
+
     /* All secondary structure annotations are dissimilar for each column
      * Set up
      *   column      1 2 3 4 
@@ -251,13 +253,14 @@ public class SecondaryStructureDistanceModelTest
      *        seq s2 F S J L
      *           ss H E E C
      */
-    else if(similar == "Not Similar") {
-        
-       anns1 = new Annotation[] { ssE, ssE, ssC, ssE};
-       anns2 = new Annotation[] { ssH, ssH, ssE, ssC};
-    
+    else if (similar == "Not Similar")
+    {
+
+      anns1 = new Annotation[] { ssE, ssE, ssC, ssE };
+      anns2 = new Annotation[] { ssH, ssH, ssE, ssC };
+
     }
-    
+
     /* All secondary structure annotations are dissimilar for each column
      * Set up
      *   column      1 2 3 4 
@@ -267,13 +270,14 @@ public class SecondaryStructureDistanceModelTest
      *        seq s2 F S J L
      *            ss H E E C
      */
-    else if(similar == "With Coil") {
-        
-      anns1 = new Annotation[] { ssE, ssE, null, ssE};
-      anns2 = new Annotation[] { ssH, ssH, ssE, null};
-    
+    else if (similar == "With Coil")
+    {
+
+      anns1 = new Annotation[] { ssE, ssE, null, ssE };
+      anns2 = new Annotation[] { ssH, ssH, ssE, null };
+
     }
-    
+
     /*  Set up
      *   column      1 2 3 4 
      *        seq s1 F R K S
@@ -282,28 +286,27 @@ public class SecondaryStructureDistanceModelTest
      *        seq s2 F S J L
      *           ss H E E C
      */
-    else {
-       
-       anns1 = new Annotation[] { ssH, ssE, ssC, ssE};
-       anns2 = new Annotation[] { ssH, ssE, ssE, ssC};
+    else
+    {
+
+      anns1 = new Annotation[] { ssH, ssE, ssC, ssE };
+      anns2 = new Annotation[] { ssH, ssE, ssE, ssC };
     }
-    
-    
-    AlignmentAnnotation ann1 = new AlignmentAnnotation("Secondary Structure",
-            "Secondary Structure", anns1);
-    AlignmentAnnotation ann2 = new AlignmentAnnotation("Secondary Structure",
-            "Secondary Structure", anns2);
-    
+
+    AlignmentAnnotation ann1 = new AlignmentAnnotation(
+            "Secondary Structure", "Secondary Structure", anns1);
+    AlignmentAnnotation ann2 = new AlignmentAnnotation(
+            "Secondary Structure", "Secondary Structure", anns2);
+
     s1.addAlignmentAnnotation(ann1);
-    s2.addAlignmentAnnotation(ann2);    
-    
+    s2.addAlignmentAnnotation(ann2);
+
     AlignmentI al = new Alignment(new SequenceI[] { s1, s2 });
     AlignFrame af = new AlignFrame(al, 300, 300);
     af.setShowSeqFeatures(true);
     af.getFeatureRenderer().findAllFeatures(true);
     return af;
   }
-  
 
   /**
    * <pre>
@@ -320,7 +323,7 @@ public class SecondaryStructureDistanceModelTest
    */
   protected AlignFrame setupAlignmentViewWithGap()
   {
-    
+
     SequenceI s1 = new Sequence("s1", "FR S");
     SequenceI s2 = new Sequence("s2", "FSJL");
 
@@ -334,39 +337,39 @@ public class SecondaryStructureDistanceModelTest
             new SequenceFeature("metal", null, 1, 4, 0f, null));
     s2.addSequenceFeature(
             new SequenceFeature("Pfam", null, 1, 4, 0f, null));
-    
-    
-    Annotation ssE = new Annotation("","",'E',0);
-    Annotation ssH = new Annotation("","",'H',0);
-    Annotation ssC = new Annotation(".","",' ',0);
-    
+
+    Annotation ssE = new Annotation("", "", 'E', 0);
+    Annotation ssH = new Annotation("", "", 'H', 0);
+    Annotation ssC = new Annotation(".", "", ' ', 0);
+
     Annotation[] anns1;
     Annotation[] anns2;
-       
-    anns1 = new Annotation[] { ssH, ssE, ssC};
-    anns2 = new Annotation[] { ssH, ssE, ssE, ssC};    
-    
-    AlignmentAnnotation ann1 = new AlignmentAnnotation("Secondary Structure",
-            "Secondary Structure", anns1);
-    AlignmentAnnotation ann2 = new AlignmentAnnotation("Secondary Structure",
-            "Secondary Structure", anns2);
-    
+
+    anns1 = new Annotation[] { ssH, ssE, ssC };
+    anns2 = new Annotation[] { ssH, ssE, ssE, ssC };
+
+    AlignmentAnnotation ann1 = new AlignmentAnnotation(
+            "Secondary Structure", "Secondary Structure", anns1);
+    AlignmentAnnotation ann2 = new AlignmentAnnotation(
+            "Secondary Structure", "Secondary Structure", anns2);
+
     s1.addAlignmentAnnotation(ann1);
-    s2.addAlignmentAnnotation(ann2);    
-        
+    s2.addAlignmentAnnotation(ann2);
+
     AlignmentI al = new Alignment(new SequenceI[] { s1, s2 });
     AlignFrame af = new AlignFrame(al, 300, 300);
     af.setShowSeqFeatures(true);
     af.getFeatureRenderer().findAllFeatures(true);
-    
+
     return af;
   }
-  
-  protected AlignFrame setupAlignmentViewWithoutSS(String type) {
-    
+
+  protected AlignFrame setupAlignmentViewWithoutSS(String type)
+  {
+
     SequenceI s1 = new Sequence("s1", "FR S");
     SequenceI s2 = new Sequence("s2", "FSJL");
-    
+
     s1.addSequenceFeature(
             new SequenceFeature("chain", null, 1, 3, 0f, null));
     s1.addSequenceFeature(
@@ -377,60 +380,61 @@ public class SecondaryStructureDistanceModelTest
             new SequenceFeature("metal", null, 1, 4, 0f, null));
     s2.addSequenceFeature(
             new SequenceFeature("Pfam", null, 1, 4, 0f, null));
-    
-    if(!type.equals("both")) {    
-      Annotation ssE = new Annotation("","",'E',0);
-      Annotation ssH = new Annotation("","",'H',0);
-      Annotation ssC = new Annotation(".","",' ',0);
-      
+
+    if (!type.equals("both"))
+    {
+      Annotation ssE = new Annotation("", "", 'E', 0);
+      Annotation ssH = new Annotation("", "", 'H', 0);
+      Annotation ssC = new Annotation(".", "", ' ', 0);
+
       Annotation[] anns1;
-        
-      anns1 = new Annotation[] { ssH, ssE, ssC};
-          
-      AlignmentAnnotation ann1 = new AlignmentAnnotation("Secondary Structure",
-              "Secondary Structure", anns1);    
-  
-      s1.addAlignmentAnnotation(ann1);    
+
+      anns1 = new Annotation[] { ssH, ssE, ssC };
+
+      AlignmentAnnotation ann1 = new AlignmentAnnotation(
+              "Secondary Structure", "Secondary Structure", anns1);
+
+      s1.addAlignmentAnnotation(ann1);
     }
-    
+
     AlignmentI al = new Alignment(new SequenceI[] { s1, s2 });
     AlignFrame af = new AlignFrame(al, 300, 300);
     af.setShowSeqFeatures(true);
     af.getFeatureRenderer().findAllFeatures(true);
     return af;
   }
-  
-  
+
   @DataProvider(name = "testData")
-  public Object[][] testData() {
-      return new Object[][] {
-              {"All Similar", 1d, 1d, 0d, 0d / 4},
-              {"Partially Similar", 1d, 1d, 0d, 0d},
-              {"Not Similar", 1d, 1d, 0d, 0d},
-              {"With Coil", 1d, 1d, 0d, 0d},
-      };
+  public Object[][] testData()
+  {
+    return new Object[][] { { "All Similar", 1d, 1d, 0d, 0d / 4 },
+        { "Partially Similar", 1d, 1d, 0d, 0d },
+        { "Not Similar", 1d, 1d, 0d, 0d },
+        { "With Coil", 1d, 1d, 0d, 0d }, };
   }
 
   @Test(dataProvider = "testData")
-  public void testFindDistances(String scenario, double expectedValue00, double expectedValue11,
-                                 double expectedValue01, double expectedValue10) {
-      AlignFrame af = setupAlignmentView(scenario);
-      AlignViewport viewport = af.getViewport();
-      AlignmentView view = viewport.getAlignmentView(false);
-
-      ScoreModelI sm = new SecondaryStructureDistanceModel();
-      sm = ScoreModels.getInstance().getScoreModel(sm.getName(),
-              af.alignPanel);
-
-      SimilarityParamsI params = new SimilarityParams(false, true, true, true);
-      params.setSecondaryStructureSource("3D Structures");
-      MatrixI distances = sm.findDistances(view, params);
-
-      assertEquals(distances.getValue(0, 0), expectedValue00);
-      assertEquals(distances.getValue(1, 1), expectedValue11);
-      assertEquals(distances.getValue(0, 1), expectedValue01);
-      assertEquals(distances.getValue(1, 0), expectedValue10);
+  public void testFindDistances(String scenario, double expectedValue00,
+          double expectedValue11, double expectedValue01,
+          double expectedValue10)
+  {
+    AlignFrame af = setupAlignmentView(scenario);
+    AlignViewport viewport = af.getViewport();
+    AlignmentView view = viewport.getAlignmentView(false);
+
+    ScoreModelI sm = new SecondaryStructureDistanceModel();
+    sm = ScoreModels.getInstance().getScoreModel(sm.getName(),
+            af.alignPanel);
+
+    SimilarityParamsI params = new SimilarityParams(false, true, true,
+            true);
+    params.setSecondaryStructureSource("3D Structures");
+    MatrixI distances = sm.findDistances(view, params);
+
+    assertEquals(distances.getValue(0, 0), expectedValue00);
+    assertEquals(distances.getValue(1, 1), expectedValue11);
+    assertEquals(distances.getValue(0, 1), expectedValue01);
+    assertEquals(distances.getValue(1, 0), expectedValue10);
   }
 
-  
 }
index 1255c58..2e36111 100644 (file)
@@ -1182,12 +1182,14 @@ public class EditCommandTest
     assertEquals(10, sf.getBegin());
     assertEquals(11, sf.getEnd());
   }
+
   private SequenceI mkDs(SequenceI as)
   {
     SequenceI ds = as.createDatasetSequence();
     ds.setSequence(ds.getSequenceAsString().toUpperCase(Locale.ROOT));
     return ds;
   }
+
   /**
    * Test that mimics 'remove all gapped columns' action. This generates a
    * series Delete Gap edits that each act on all sequences that share a gapped
@@ -1210,35 +1212,38 @@ public class EditCommandTest
      * and check we are preserving data - if the calls below fail, something has broken the Jalview dataset derivation process
      */
     assertEquals("ABCDEF", seq1.getDatasetSequence().getSequenceAsString());
-    assertEquals(original1,seq1.getSequenceAsString());
-    SequenceI seq2 = new Sequence("sq2",original2);
+    assertEquals(original1, seq1.getSequenceAsString());
+    SequenceI seq2 = new Sequence("sq2", original2);
     SequenceI ds2 = mkDs(seq2);
     SequenceI seq3 = new Sequence("sq3", original3);
     SequenceI ds3 = mkDs(seq3);
-    List<SequenceI> sqs = Arrays.asList( seq1, seq2, seq3 );
+    List<SequenceI> sqs = Arrays.asList(seq1, seq2, seq3);
     Alignment al = new Alignment(sqs.toArray(new SequenceI[0]));
-    EditCommand lefj = new JustifyLeftOrRightCommand("Left J", true, sqs, 1, 7, al);
+    EditCommand lefj = new JustifyLeftOrRightCommand("Left J", true, sqs, 1,
+            7, al);
     String exp = "-ABcD---EF";
     // check without case conservation
-    assertEquals(exp.toUpperCase(Locale.ROOT),seq1.getSequenceAsString().toUpperCase(Locale.ROOT));
+    assertEquals(exp.toUpperCase(Locale.ROOT),
+            seq1.getSequenceAsString().toUpperCase(Locale.ROOT));
     // check case
-    assertEquals(exp,seq1.getSequenceAsString());
+    assertEquals(exp, seq1.getSequenceAsString());
     // and other seqs
-    assertEquals("-GHiJ---",seq2.getSequenceAsString());
-    assertEquals("-MNoP---Q",seq3.getSequenceAsString());
-    lefj.undoCommand(new AlignmentI[] { al});
-    assertEquals(original3,seq3.getSequenceAsString());
-    assertEquals(original1,seq1.getSequenceAsString());
-    assertEquals(original2,seq2.getSequenceAsString());
-    
-    EditCommand righj = new JustifyLeftOrRightCommand("Right J", false, sqs, 2, 7, al);
-    assertEquals("----ABcDEF",seq1.getSequenceAsString());
-    assertEquals("-G---HiJ",seq2.getSequenceAsString());
-    assertEquals("-M---NoPQ",seq3.getSequenceAsString());
-    righj.undoCommand(new AlignmentI[] { al});
-    assertEquals(original3,seq3.getSequenceAsString());
-    assertEquals(original1,seq1.getSequenceAsString());
-    assertEquals(original2,seq2.getSequenceAsString());
-        
+    assertEquals("-GHiJ---", seq2.getSequenceAsString());
+    assertEquals("-MNoP---Q", seq3.getSequenceAsString());
+    lefj.undoCommand(new AlignmentI[] { al });
+    assertEquals(original3, seq3.getSequenceAsString());
+    assertEquals(original1, seq1.getSequenceAsString());
+    assertEquals(original2, seq2.getSequenceAsString());
+
+    EditCommand righj = new JustifyLeftOrRightCommand("Right J", false, sqs,
+            2, 7, al);
+    assertEquals("----ABcDEF", seq1.getSequenceAsString());
+    assertEquals("-G---HiJ", seq2.getSequenceAsString());
+    assertEquals("-M---NoPQ", seq3.getSequenceAsString());
+    righj.undoCommand(new AlignmentI[] { al });
+    assertEquals(original3, seq3.getSequenceAsString());
+    assertEquals(original1, seq1.getSequenceAsString());
+    assertEquals(original2, seq2.getSequenceAsString());
+
   }
 }
index 4a6036f..4923c8f 100644 (file)
@@ -326,7 +326,10 @@ public class EnsemblGeneTest
     assertTrue(geneIds.contains("ENSRNOG00000010957")); // rat
     assertTrue(geneIds.contains("ENSXETG00000004845")); // xenopus
     assertTrue(geneIds.contains("ENSDARG00000017661")); // zebrafish
-    assertTrue(geneIds.contains("ENSGALG00000012865")); // chicken
+    // was ENSGALG00000012865 - which is still the canonical uniprot BRAF cross
+    // reference via GRCg6a
+    assertTrue(geneIds.contains("ENSGALG00010013466")); // chicken BRAF
+                                                        // bGalGal1.mat.broiler.GRCg7b
     assertEquals(8, geneIds.size());
 
   }
index 0e6791b..c1b5b6e 100644 (file)
@@ -20,7 +20,6 @@
  */
 package jalview.gui;
 
-
 import java.awt.Color;
 import java.io.File;
 import java.util.Iterator;
@@ -92,18 +91,14 @@ public class AssociatePDBFileTest
             + "KVRLYSIASSAIGDFGDSKTVSLCVKRLIYTNDAGEIVKGVCSNFLCDLQPGDNVQITGPVGKEMLMPKDPN\n"
             + "ATIIMLATGTGIAPFRSFLWKMFFEKHDDYKFNGLGWLFLGVPTSSSLLYKEEFGKMKERAPENFRVDYAVS\n"
             + "REQTNAAGERMYIQTRMAEYKEELWELLKKDNTYVYMCGLKGMEKGIDDIMVSLAEKDGIDWFDYKKQLKRG\n"
-            + "DQWNVEVY\n"
-            + ">1GAQ|B/1-98\n"
+            + "DQWNVEVY\n" + ">1GAQ|B/1-98\n"
             + "ATYNVKLITPEGEVELQVPDDVYILDQAEEDGIDLPYSCRAGSCSSCAGKVVSGSVDQSDQSYLDDGQIADG\n"
-            + "WVLTCHAYPTSDVVIETHKEEELTGA\n"
-            + ">1GAQ|C/19-314\n"
+            + "WVLTCHAYPTSDVVIETHKEEELTGA\n" + ">1GAQ|C/19-314\n"
             + "ESKKQEEGVVTNLYKPKEPYVGRCLLNTKITGDDAPGETWHMVFSTEGKIPYREGQSIGVIADGVDKNGKPH\n"
             + "KVRLYSIASSAIGDFGDSKTVSLCVKRLIYTNDAGEIVKGVCSNFLCDLQPGDNVQITGPVGKEMLMPKDPN\n"
             + "ATIIMLATGTGIAPFRSFLWKMFFEKHDDYKFNGLGWLFLGVPTSSSLLYKEEFGKMKERAPENFRVDYAVS\n"
             + "REQTNAAGERMYIQTRMAEYKEELWELLKKDNTYVYMCGLKGMEKGIDDIMVSLAEKDGIDWFDYKKQLKRG\n"
-            + "DQWNVEVY\n"
-            ,
-            DataSourceType.PASTE);
+            + "DQWNVEVY\n", DataSourceType.PASTE);
 
     /*
      * wait for Consensus thread to complete
@@ -128,15 +123,15 @@ public class AssociatePDBFileTest
   @Test(groups = "Functional")
   public void testAssociatePDBFile()
   {
-    String assoc_file="examples/1gaq.txt";
-    for (SequenceI toassoc:af.getViewport().getAlignment().getSequences())
+    String assoc_file = "examples/1gaq.txt";
+    for (SequenceI toassoc : af.getViewport().getAlignment().getSequences())
     {
-      PDBEntry pe = new AssociatePdbFileWithSeq()
-              .associatePdbWithSeq(assoc_file,
-                      DataSourceType.FILE, toassoc, false,
-                      Desktop.instance);
+      PDBEntry pe = new AssociatePdbFileWithSeq().associatePdbWithSeq(
+              assoc_file, DataSourceType.FILE, toassoc, false,
+              Desktop.instance);
       Assert.assertNotNull(pe);
-      Assert.assertNotEquals(toassoc.getDatasetSequence().getAnnotation().length,0);
+      Assert.assertNotEquals(
+              toassoc.getDatasetSequence().getAnnotation().length, 0);
     }
   }
 }
index ac02c03..e6316f3 100644 (file)
@@ -55,29 +55,33 @@ public class CalculationChooserTest
      * peptide models for PCA
      */
     List<ScoreModelI> filtered = CalculationChooser
-            .getApplicableScoreModels(false, true, true,false);
+            .getApplicableScoreModels(false, true, true, false);
     assertEquals(filtered.size(), 5);
     assertSame(filtered.get(0), blosum62);
     assertSame(filtered.get(1), pam250);
     assertEquals(filtered.get(2).getName(), "PID");
     assertEquals(filtered.get(3).getName(), "Sequence Feature Similarity");
-    assertEquals(filtered.get(4).getName(), "Secondary Structure Similarity");
+    assertEquals(filtered.get(4).getName(),
+            "Secondary Structure Similarity");
 
     /*
      * peptide models for Tree are the same
      */
-    filtered = CalculationChooser.getApplicableScoreModels(false, false, true,false);
+    filtered = CalculationChooser.getApplicableScoreModels(false, false,
+            true, false);
     assertEquals(filtered.size(), 5);
     assertSame(filtered.get(0), blosum62);
     assertSame(filtered.get(1), pam250);
     assertEquals(filtered.get(2).getName(), "PID");
     assertEquals(filtered.get(3).getName(), "Sequence Feature Similarity");
-    assertEquals(filtered.get(4).getName(), "Secondary Structure Similarity");
+    assertEquals(filtered.get(4).getName(),
+            "Secondary Structure Similarity");
 
     /*
      * nucleotide models for PCA
      */
-    filtered = CalculationChooser.getApplicableScoreModels(true, true, false,false);
+    filtered = CalculationChooser.getApplicableScoreModels(true, true,
+            false, false);
     assertEquals(filtered.size(), 3);
     assertSame(filtered.get(0), dna);
     assertEquals(filtered.get(1).getName(), "PID");
@@ -86,7 +90,8 @@ public class CalculationChooserTest
     /*
      * nucleotide models for Tree are the same
      */
-    filtered = CalculationChooser.getApplicableScoreModels(true, false, false,false);
+    filtered = CalculationChooser.getApplicableScoreModels(true, false,
+            false, false);
     assertEquals(filtered.size(), 3);
     assertSame(filtered.get(0), dna);
     assertEquals(filtered.get(1).getName(), "PID");
@@ -101,30 +106,37 @@ public class CalculationChooserTest
     /*
      * nucleotide models for Tree are unchanged
      */
-    filtered = CalculationChooser.getApplicableScoreModels(true, false, true,false);
+    filtered = CalculationChooser.getApplicableScoreModels(true, false,
+            true, false);
     assertEquals(filtered.size(), 4);
     assertSame(filtered.get(0), dna);
     assertEquals(filtered.get(1).getName(), "PID");
     assertEquals(filtered.get(2).getName(), "Sequence Feature Similarity");
-    assertEquals(filtered.get(3).getName(), "Secondary Structure Similarity");
+    assertEquals(filtered.get(3).getName(),
+            "Secondary Structure Similarity");
 
     /*
      * nucleotide models for PCA add BLOSUM62 as last option
      */
-    filtered = CalculationChooser.getApplicableScoreModels(true, true, false,false);
+    filtered = CalculationChooser.getApplicableScoreModels(true, true,
+            false, false);
     assertEquals(filtered.size(), 4);
     assertSame(filtered.get(0), dna);
     assertEquals(filtered.get(1).getName(), "PID");
     assertEquals(filtered.get(2).getName(), "Sequence Feature Similarity");
     assertSame(filtered.get(3), blosum62);
-    
-    filtered = CalculationChooser.getApplicableScoreModels(true, true, false,true);
+
+    filtered = CalculationChooser.getApplicableScoreModels(true, true,
+            false, true);
     assertEquals(filtered.size(), 1); // DNA matrix for DNA pasimap
-    for (int i=0; i<=8;i++)
+    for (int i = 0; i <= 8; i++)
     {
-      boolean isDna = (i & 1) != 0,isPca = (i & 2) != 0,isSS = (i & 4) != 0;
-     
-      assertEquals(CalculationChooser.getApplicableScoreModels(isDna,isPca,isSS,true).size(), (isDna) ? 1: 2);
+      boolean isDna = (i & 1) != 0, isPca = (i & 2) != 0,
+              isSS = (i & 4) != 0;
+
+      assertEquals(CalculationChooser
+              .getApplicableScoreModels(isDna, isPca, isSS, true).size(),
+              (isDna) ? 1 : 2);
     }
   }
 }
index 8b2fa74..ca90d8a 100644 (file)
@@ -250,9 +250,8 @@ public class Jalview2xmlTests extends Jalview2xmlBase
 
     boolean diffseqcols = false, diffgseqcols = false;
     SequenceI[] sqs = af.getViewport().getAlignment().getSequencesArray();
-    for (int p = 0,
-            pSize = af.getViewport().getAlignment().getWidth(); p < pSize
-                    && (!diffseqcols || !diffgseqcols); p++)
+    for (int p = 0, pSize = af.getViewport().getAlignment()
+            .getWidth(); p < pSize && (!diffseqcols || !diffgseqcols); p++)
     {
       if (_rcs.findColour(sqs[0].getCharAt(p), p, sqs[0], null, 0f) != _rcs
               .findColour(sqs[5].getCharAt(p), p, sqs[5], null, 0f))
@@ -271,9 +270,8 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     assertTrue(__rcs.isSeqAssociated(),
             "Group Annotation colourscheme wasn't sequence associated");
 
-    for (int p = 0,
-            pSize = af.getViewport().getAlignment().getWidth(); p < pSize
-                    && (!diffseqcols || !diffgseqcols); p++)
+    for (int p = 0, pSize = af.getViewport().getAlignment()
+            .getWidth(); p < pSize && (!diffseqcols || !diffgseqcols); p++)
     {
       if (_rgcs.findColour(sqs[1].getCharAt(p), p, sqs[1], null,
               0f) != _rgcs.findColour(sqs[2].getCharAt(p), p, sqs[2], null,
index 9ff93e1..a198d3c 100644 (file)
@@ -345,7 +345,7 @@ public class ResidueShaderTest
                   SecondaryStructureCount secondaryStructureCount)
           {
             // TODO Auto-generated method stub
-            
+
           }
 
           @Override
index d476d6e..983f94c 100644 (file)
@@ -151,6 +151,7 @@ public class ClustalxColourSchemeTest
     // viewport
     // assertEquals(cs.findColour('C', 0, al.getSequenceAt(0)), clustalPink);
   }
+
   @Test
   public void testDocString()
   {
index 3cc9ca6..62dc73e 100644 (file)
@@ -77,6 +77,11 @@ public class FileUtilsTest
               + d + "' contains '" + notInDirname + "' when it shouldn't");
   }
 
+  /**
+   * these need to be maintained as jalview's source base grows
+   * 
+   * @return
+   */
   @DataProvider(name = "patternsAndMinNumFiles")
   public Object[][] patternsAndMinNumFiles()
   {
@@ -90,7 +95,7 @@ public class FileUtilsTest
         { "test/jalview/**/F*.java", 18, 30 }, // 20 at time of writing
         { "test/jalview/util/F**.java", 1, 5 }, // 2 at time of writing
         { "src/jalview/b*/*.java", 14, 19 }, // 15 at time of writing
-        { "src/jalview/b**/*.java", 20, 25 }, // 22 at time of writing
+        { "src/jalview/b**/*.java", 23, 26 }, // 22 at time of writing
     };
   }
 
index cfb5ab0..04acbc3 100644 (file)
@@ -36,3 +36,4 @@ install4j_background_image_text_suffix_cmd = font-size 30 text 194,218 '%s'
 install4j_background_image_text_commit_cmd = text 18,170 '%s'
 install4j_background_image_text_date_cmd = text 18,190 '%s'
 
+install4j_title_icon = jalview_logo-64.png
diff --git a/utils/channels/default/images/jalview_logo-64.png b/utils/channels/default/images/jalview_logo-64.png
new file mode 100644 (file)
index 0000000..c5b5a3c
Binary files /dev/null and b/utils/channels/default/images/jalview_logo-64.png differ
diff --git a/utils/channels/default/images/jalview_logo-64@2x.png b/utils/channels/default/images/jalview_logo-64@2x.png
new file mode 100644 (file)
index 0000000..34748ed
Binary files /dev/null and b/utils/channels/default/images/jalview_logo-64@2x.png differ
index b39dc99..671a339 100644 (file)
@@ -48,3 +48,4 @@ install4j_background_image_text_suffix_cmd = font-size 30 text 194,218 '%s'
 install4j_background_image_text_commit_cmd = text 18,250 '%s'
 install4j_background_image_text_date_cmd = text 18,270 '%s'
 
+install4j_title_icon = jalview_develop_logo-64.png
diff --git a/utils/channels/develop-SUFFIX/images/jalview_develop_logo-64.png b/utils/channels/develop-SUFFIX/images/jalview_develop_logo-64.png
new file mode 100644 (file)
index 0000000..54d7f89
Binary files /dev/null and b/utils/channels/develop-SUFFIX/images/jalview_develop_logo-64.png differ
diff --git a/utils/channels/develop-SUFFIX/images/jalview_develop_logo-64@2x.png b/utils/channels/develop-SUFFIX/images/jalview_develop_logo-64@2x.png
new file mode 100644 (file)
index 0000000..e7e3928
Binary files /dev/null and b/utils/channels/develop-SUFFIX/images/jalview_develop_logo-64@2x.png differ
index 2c152d0..cd356ab 100644 (file)
@@ -48,3 +48,4 @@ install4j_background_image_text_suffix_cmd = font-size 30 text 194,218 '%s'
 install4j_background_image_text_commit_cmd = text 18,250 '%s'
 install4j_background_image_text_date_cmd = text 18,270 '%s'
 
+install4j_title_icon = jalview_develop_logo-64.png
diff --git a/utils/channels/develop/images/jalview_develop_logo-64.png b/utils/channels/develop/images/jalview_develop_logo-64.png
new file mode 100644 (file)
index 0000000..54d7f89
Binary files /dev/null and b/utils/channels/develop/images/jalview_develop_logo-64.png differ
diff --git a/utils/channels/develop/images/jalview_develop_logo-64@2x.png b/utils/channels/develop/images/jalview_develop_logo-64@2x.png
new file mode 100644 (file)
index 0000000..e7e3928
Binary files /dev/null and b/utils/channels/develop/images/jalview_develop_logo-64@2x.png differ
index 6f3991b..4367f3f 100644 (file)
@@ -32,3 +32,5 @@ install4j_background_image_text_suffix_cmd = font-size 30 text 194,218 '%s'
 install4j_background_image_text_commit_cmd = text 18,170 '%s'
 install4j_background_image_text_date_cmd = text 18,190 '%s'
 
+install4j_title_icon = jalview_logo-64.png
+install4j_title_icon = jalview_logo-64.png
diff --git a/utils/channels/jalviewjs/images/jalview_logo-64.png b/utils/channels/jalviewjs/images/jalview_logo-64.png
new file mode 100644 (file)
index 0000000..57c7a9e
Binary files /dev/null and b/utils/channels/jalviewjs/images/jalview_logo-64.png differ
diff --git a/utils/channels/jalviewjs/images/jalview_logo-64@2x.png b/utils/channels/jalviewjs/images/jalview_logo-64@2x.png
new file mode 100644 (file)
index 0000000..869701d
Binary files /dev/null and b/utils/channels/jalviewjs/images/jalview_logo-64@2x.png differ
index 27fc1a4..904edf1 100644 (file)
@@ -37,3 +37,4 @@ install4j_background_image_text_suffix_cmd = font-size 30 text 194,218 '%s'
 install4j_background_image_text_commit_cmd = text 18,250 '%s'
 install4j_background_image_text_date_cmd = text 18,270 '%s'
 
+install4j_title_icon = jalview_logo-64.png
diff --git a/utils/channels/release/images/jalview_logo-64.png b/utils/channels/release/images/jalview_logo-64.png
new file mode 100644 (file)
index 0000000..23a9ce2
Binary files /dev/null and b/utils/channels/release/images/jalview_logo-64.png differ
diff --git a/utils/channels/release/images/jalview_logo-64@2x.png b/utils/channels/release/images/jalview_logo-64@2x.png
new file mode 100644 (file)
index 0000000..42727e0
Binary files /dev/null and b/utils/channels/release/images/jalview_logo-64@2x.png differ
index f0c8e2a..b774de5 100644 (file)
@@ -37,3 +37,4 @@ install4j_background_image_text_suffix_cmd = font-size 30 text 194,218 '%s'
 install4j_background_image_text_commit_cmd = text 18,250 '%s'
 install4j_background_image_text_date_cmd = text 18,270 '%s'
 
+install4j_title_icon = jalview_test-release_logo-64.png
diff --git a/utils/channels/test-release/images/jalview_test-release_logo-64.png b/utils/channels/test-release/images/jalview_test-release_logo-64.png
new file mode 100644 (file)
index 0000000..5f79cb9
Binary files /dev/null and b/utils/channels/test-release/images/jalview_test-release_logo-64.png differ
diff --git a/utils/channels/test-release/images/jalview_test-release_logo-64@2x.png b/utils/channels/test-release/images/jalview_test-release_logo-64@2x.png
new file mode 100644 (file)
index 0000000..8185b9b
Binary files /dev/null and b/utils/channels/test-release/images/jalview_test-release_logo-64@2x.png differ
index 1098010..ade0089 100755 (executable)
@@ -2,7 +2,6 @@
 
 # save args and first parameter
 $myArgs = $args.Clone()
-$myArg1 = $args[0]
 
 # setup for powershell version < 6.0
 [bool] $myIsWindows = 0
@@ -16,35 +15,67 @@ if ( $IsWindows -eq $null ) {
   $myIsMacOS = $IsMacOS
 }
 
-# parent dir of this actual script (which should be the getdown appdir/bin). Follow all symlinks.  Like GNU readlink -f
+[bool] $headless = 0
+[bool] $gui = 0
+[bool] $help = 0
+[bool] $debug = 0
+Switch -Regex ($args) {
+  "--help|--help-|--version|-h" {
+    $help = 1
+    $headless = 1
+    Continue
+  }
+  "--gui" {
+    $gui = 1
+    Continue
+  }
+  "--headless" {
+    $headless = 1
+    Continue
+  }
+  "--debug" {
+    $debug = 1
+    Continue
+  }
+}
+if ( $help ) {
+  # --help takes precedence
+  $gui = 0
+} elseif ( $gui ) {
+  # --gui takes precedence over --headless
+  $headless = 0
+}
+
+# actual path of this script (which should be the getdown appdir/bin). Follow all symlinks.  Like GNU readlink -f
 function Readlink-f {
-  Param($Link)
-  $Return = $null
-  $c = 0
-  $max = 100 # just in case we end up in a loop
-  [bool] $found = 0
-  $file = Get-Item -Path $Link
-  $prevfile = $null
-  While ( $c -lt $max -and "${file}" -ne "${prevfile}" -and -not $found ) {
-    $prevfile = $file
-    [string] $target = ( $file ).Target
-    If ( $target -eq $null -or ( $file ).LinkType -ne "SymbolicLink" ) {
-      $Return = $file
-      $found = 1
-    } Else {
-      If ( $( Split-Path -Path $target -IsAbsolute ) ) {
-        $file = Get-Item -Path $target
-      } Else {
-# symbolic link is relative: combine previous link parent dir with the link target and resolve
-        $file = Get-Item -Path ( Join-Path -Path ( Split-Path -Path $prevfile -Parent ) -ChildPath $target -Resolve )
-      }
+  Param(
+    [Parameter(mandatory=$true, ValueFromPipeline=$true)]$Link,
+    [Parameter()]$iteration_count = 1
+  )
+  if ( $iteration_count -ge 100 ) {
+    Write-Error "Readlink-f iterated 100 times"
+    return $Link
+  }
+  if ( $Link -eq "" -or $Link -eq $null ) {
+    return $null
+  }
+  $path_components = @()
+  $dir = Get-Item $Link
+  while ( $dir -ne $null ) {
+    while ( $dir.Target -ne $null ) {
+      # [System.IO.Path]::Combine caters for a multitude of sins that it's almost impossible to deal with with Join-Path
+      $dir = Get-Item ([System.IO.Path]::GetFullPath( [System.IO.Path]::Combine( (Split-Path $dir -Parent), $dir.Target )))
     }
-    $c++
+    $parent = Split-Path -Path $dir -Parent
+    $path_components = @( (Split-Path -Path $dir -Leaf) ) + $path_components
+    $dir = Readlink-f $parent ($iteration_count + 1)
   }
-  if ( -not $found ) {
-    throw "Could not determine path to actual file $( Split-Path -Path $Link -Leaf )"
+  $real = Get-Item "/"
+  foreach ( $component in $path_components) {
+    # [System.IO.Path]::Combine caters for a multitude of sins that it's almost impossible to deal with with Join-Path
+    $real = Get-Item ([System.IO.Path]::GetFullPath( [System.IO.Path]::Combine( $real, $component )))
   }
-  $Return
+  $real
 }
 
 # Avert problem with unix version of powershell and tell user the reason (Windows must always have .ps1 extension)
@@ -52,14 +83,28 @@ if ( $MyInvocation.MyCommand.Path -eq $null ) {
   throw "Script or link to script must have extension .ps1"
 }
 
+# args for the JVM
+$JVMARGS = @()
 
 $CMDPATH = ( Get-Item $MyInvocation.MyCommand.Path )
 $SCRIPTPATH = Readlink-f -Link $CMDPATH
+$SCRIPT = $SCRIPTPATH
 $DIR = Split-Path -Path $SCRIPTPATH -Parent
-
 $APPDIR = If ( ( Split-Path -Path $DIR -Leaf ) -eq "bin" ) { Split-Path -Path $DIR -Parent } Else { $DIR }
 $JAVAEXE = If ( $myIsWindows ) { "java.exe" } Else { "java" }
 $JAVA = Join-Path -Path $APPDIR -ChildPath ( "jre/" + $( If ( $myIsMacOS ) { "Contents/Home/" } Else { "" } ) + "bin/${JAVAEXE}" )
+
+if ( $headless ) {
+  # not setting java.awt.headless in java invocation of running jalview due to problem with Jmol
+  if ( $help ) {
+    $JVMARGS += "-Djava.awt.headless=true"
+  }
+  # this suppresses the Java icon appearing in the macOS Dock
+  if ( $myIsMacOS ) {
+    $JVMARGS += "-Dapple.awt.UIElement=true"
+  }
+}
+
 $GETDOWNTXT = Join-Path -Path $APPDIR -ChildPath "getdown.txt"
 
 # look for getdown.txt -- needed to create classpath
@@ -67,22 +112,39 @@ if ( -not ( Test-Path -Path "${GETDOWNTXT}" ) ) {
   throw "Cannot find ${GETDOWNTXT}"
 }
 
+# launching Jalview with jalview.bin.Launcher instead of getdown-launcher.jar
+$CLASS = "jalview.bin.Launcher"
+
+# get CLASSPATH from the code= entries in getdown.txt
+$CLASSPATH = ( Select-String -Path "${GETDOWNTXT}" -AllMatches -Pattern "code\s*=\s*(.*)$" | foreach { Join-Path -Path $APPDIR -ChildPath $($_.Matches.Groups[1].Value ) } ) -join $( If ( $myIsWindows ) { ";" } Else { ":" } )
+
+# get console width
+$COLUMNS = $Host.UI.RawUI.WindowSize.Width
+$JVMARGS += "-DCONSOLEWIDTH=${COLUMNS}"
+$JVMARGS += "-Dgetdownappdir=${APPDIR}"
+$JVMARGS += "-Dinstaller.appdir=${APPDIR}"
+$JVMARGS += "-Dlauncher.script=${SCRIPT}"
+
 # look for bundled JRE. Might not be there if unix installer used in which case just invoke "java"
 if ( -not ( Test-Path -Path "${JAVA}" ) ) {
-  Write-Host "Cannot find bundled ${JAVAEXE}. Using system ${JAVAEXE} and hoping for the best!"
+  Write-Host "Cannot find bundled ${JAVA}. Using system ${JAVAEXE} and hoping for the best!"
   $JAVA = $JAVAEXE
 }
 
-$CLASSPATH = ( Select-String -Path "${GETDOWNTXT}" -AllMatches -Pattern "code\s*=\s*(.*)$" | foreach { Join-Path -Path $APPDIR -ChildPath $($_.Matches.Groups[1].Value ) } ) -join $( If ( $myIsWindows ) { ";" } Else { ":" } )
-
-# get console width
-$CONSOLEWIDTH = $Host.UI.RawUI.WindowSize.Width
+# we should always have at least one JVMARGS so this is okay
+$myJvmArgsString = '"' + $($JVMARGS -join '" "') + '"'
 
-# quote the args and the command (in case of spaces) with escape chars (`) and precede with & to indicate command not string
+# quote the args and the command (in case of spaces) with escape chars (`) and precede with & to indicate a command not string
 if ( $myArgs.count -eq 0 ) {
-  Invoke-Expression -Command "& `"${JAVA}`" `"-DCONSOLEWIDTH=${CONSOLEWIDTH}`" `"-Dgetdownappdir=${APPDIR}`" -cp `"${CLASSPATH}`" jalview.bin.Launcher"
+  $COMMAND = "& `"${JAVA}`" ${myJvmArgsString} -cp `"${CLASSPATH}`" $CLASS"
 } else {
   $myArgsString = '"' + $($myArgs -join '" "') + '"'
-  Invoke-Expression -Command "& `"${JAVA}`" `"-DCONSOLEWIDTH=${CONSOLEWIDTH}`" `"-Dgetdownappdir=${APPDIR}`" -cp `"${CLASSPATH}`" jalview.bin.Launcher ${myArgsString}"
+  $COMMAND = "& `"${JAVA}`" ${myJvmArgsString} -cp `"${CLASSPATH}`" $CLASS ${myArgsString}"
 }
 
+if ( $debug -or $help ) {
+  Write-Error -Message "Shell running: ${COMMAND}"
+}
+
+Invoke-Expression -Command ${COMMAND}
+
index 90d1558..3880faf 100755 (executable)
@@ -2,34 +2,6 @@
 
 declare -a ARGS=("${@}")
 
-# this whole next part is because there's no readlink -f in Darwin
-function readlinkf() {
-  FINDFILE="$1"
-  FILE="${FINDFILE}"
-  PREVFILE=""
-  C=0
-  MAX=100 # just in case we end up in a loop
-  FOUND=0
-  while [ "${C}" -lt "${MAX}" -a "${FILE}" != "${PREVFILE}" -a "${FOUND}" -ne 1 ]; do
-    PREVFILE="${FILE}"
-    FILE="$(readlink "${FILE}")"
-    if [ -z "${FILE}" ]; then
-      # the readlink is empty means we've arrived at the script, let's canonicalize with pwd
-      FILE="$(cd "$(dirname "${PREVFILE}")" &> /dev/null && pwd -P)"/"$(basename "${PREVFILE}")"
-      FOUND=1
-    elif [ "${FILE#/}" = "${FILE}" ]; then
-      # FILE is not an absolute path link, we need to add the relative path to the previous dir
-      FILE="$(dirname "${PREVFILE}")/${FILE}"
-    fi
-    C=$((C+1))
-  done
-  if [ "${FOUND}" -ne 1 ]; then
-    echo "Could not determine path to actual file '$(basename "${FINDFILE}")'" >&2
-    exit 1
-  fi
-  echo "${FILE}"
-}
-
 ISMACOS=0
 if [ "$( uname -s )" = "Darwin" ]; then
   ISMACOS=1
@@ -48,6 +20,7 @@ for RAWARG in "${@}"; do
       ;;
     --help|--help-*|--version|-h)
       HELP=1
+      HEADLESS=1
       ;;
     --gui)
       GUI=1
@@ -56,33 +29,85 @@ for RAWARG in "${@}"; do
       DEBUG=1
       ;;
   esac
-  
-  if [ "${HELP}" = 1 ]; then
-    # --help takes precedence
-    HEADLESS=1
-    GUI=0
-  elif [ "${GUI}" = 1 ]; then
-    # --gui takes precedence over --headless
-    HEADLESS=0
-  fi
 done
+if [ "${HELP}" = 1 ]; then
+  # --help takes precedence
+  GUI=0
+elif [ "${GUI}" = 1 ]; then
+  # --gui takes precedence over --headless
+  HEADLESS=0
+fi
+
+# this whole next part is because there's no readlink -f in Darwin
+function readlinkf() {
+  FINDFILE="$1"
+  FILE="${FINDFILE}"
+  PREVFILE=""
+  C=0
+  MAX=100 # just in case we end up in a loop
+  FOUND=0
+  while [ "${C}" -lt "${MAX}" -a "${FILE}" != "${PREVFILE}" -a "${FOUND}" -ne 1 ]; do
+    PREVFILE="${FILE}"
+    FILE="$(readlink "${FILE}")"
+    if [ -z "${FILE}" ]; then
+      # the readlink is empty means we've arrived at the script, let's canonicalize with pwd
+      FILE="$(cd "$(dirname "${PREVFILE}")" &> /dev/null && pwd -P)"/"$(basename "${PREVFILE}")"
+      FOUND=1
+    elif [ "${FILE#/}" = "${FILE}" ]; then
+      # FILE is not an absolute path link, we need to add the relative path to the previous dir
+      FILE="$(dirname "${PREVFILE}")/${FILE}"
+    fi
+    C=$((C+1))
+  done
+  if [ "${FOUND}" -ne 1 ]; then
+    echo "Could not determine path to actual file '$(basename "${FINDFILE}")'" >&2
+    exit 1
+  fi
+  echo "${FILE}"
+}
 
+# args for the JVM
 declare -a JVMARGS=()
 
+JAVABIN=""
 # set vars for being inside the macos App Bundle
 if [ "${ISMACOS}" = 1 ]; then
 # MACOS ONLY
-  DIR="$(dirname "$(readlinkf "$0")")"
+  SCRIPT="$(readlinkf "$0")"
+  DIR="$(dirname "${SCRIPT}")"
   APPDIR="${DIR%/bin}"
-  JAVA="${APPDIR}/jre/Contents/Home/bin/java"
-  JVMARGS=( "${JVMARGS[@]}" "-Xdock:icon=${APPDIR}/resource/jalview_logo.png" )
+
+  if [ -d "${APPDIR}/jre" ]; then
+    JREDIR="${APPDIR}/jre"
+  elif [ -e "${APPDIR}/installer.properties" ]; then
+    INSTALLERAPPDIR="$( grep -E "^installer.appdir[[:space:]]*=[[:space:]]*" "${APPDIR}/installer.properties" | sed -E 's/^installer.appdir[[:space:]]*=[[:space:]]*//' )"
+    if [ -d "${INSTALLERAPPDIR}/jre" ]; then
+      JREDIR="${INSTALLERAPPDIR}/jre"
+    fi
+  fi
+  if [ ! -z "$JREDIR" ]; then
+    JAVABIN="${JREDIR}/Contents/Home/bin"
+  fi
+  if [ "${HEADLESS}" != 1 ]; then
+    JVMARGS=( "${JVMARGS[@]}" "-Xdock:icon=${APPDIR}/resource/jalview_logo.png" )
+  fi
 else
 # NOT MACOS
-  DIR="$(dirname "$(readlink -f "$0")")"
+  SCRIPT="$(readlink -f "$0")"
+  DIR="$(dirname "${SCRIPT}")"
   APPDIR="${DIR%/bin}"
-  JAVA="${APPDIR}/jre/bin/java"
+  if [ -d "${APPDIR}/jre" ]; then
+    JAVABIN="${APPDIR}/jre/bin"
+  elif [ -e "${APPDIR}/installer.properties" ]; then
+    INSTALLERAPPDIR="$( grep -E "^installer.appdir[[:space:]]*=[[:space:]]*" "${APPDIR}/installer.properties" | sed -E 's/^installer.appdir[[:space:]]*=[[:space:]]*//' )"
+    if [ ! -z "$INSTALLERAPPDIR" -a -d "${INSTALLERAPPDIR}/jre" ]; then
+      JAVABIN="${INSTALLERAPPDIR}/jre/bin"
+    fi
+  fi
 fi
+JAVA="${JAVABIN}/java"
 
+# headless java arguments
 if [ "${HEADLESS}" = 1 ]; then
   # not setting java.awt.headless in java invocation of running jalview due to problem with Jmol
   if [ "${HELP}" = 1 ]; then
@@ -96,27 +121,50 @@ fi
 
 SYSJAVA=java
 GETDOWNTXT="${APPDIR}/getdown.txt"
+CHANNELPROPS="${APPDIR}/channel.props"
+NAME="$( grep app_name= "${CHANNELPROPS}" | cut -d= -f2 )"
 
 CLASSPATH=""
 # save an array of JAR paths in case we're in WSL (see later)
 declare -a JARPATHS=()
-if [ -e "${GETDOWNTXT}" ]; then
-  # always check grep and sed regexes on macos -- they're not the same
-for JAR in $(grep -e '^code[[:space:]]*=[[:space:]]*' "${GETDOWNTXT}" | while read -r line; do echo $line | sed -E -e 's/code[[:space:]]*=[[:space:]]*//;'; done);
-  do
-    [ -n "${CLASSPATH}" ] && CLASSPATH="${CLASSPATH}:"
-    CLASSPATH="${CLASSPATH}${APPDIR}/${JAR}"
-    JARPATHS=( "${JARPATHS[@]}" "${APPDIR}/${JAR}" )
-  done
-else
-  echo "Cannot find getdown.txt" >&2
+
+# look for getdown.txt -- needed to create classpath
+if [ ! -e "${GETDOWNTXT}" ]; then
+  echo "Cannot find ${GETDOWNTXT}" >&2
   exit 3
 fi
 
-# WINDOWS ONLY (Cygwin or WSL)
-# change paths for Cygwin or Windows Subsystem for Linux (WSL)
+# launching Jalview with jalview.bin.Launcher instead of getdown-launcher.jar
+CLASS="jalview.bin.Launcher"
+
+# get classpath from the code= entries in getdown.txt
+# always check grep and sed regexes on macos -- they're not the same
+for JAR in $(grep -e '^code[[:space:]]*=[[:space:]]*' "${GETDOWNTXT}" | while read -r line; do echo $line | sed -E -e 's/code[[:space:]]*=[[:space:]]*//;'; done);
+do
+  [ -n "${CLASSPATH}" ] && CLASSPATH="${CLASSPATH}:"
+  CLASSPATH="${CLASSPATH}${APPDIR}/${JAR}"
+  JARPATHS=( "${JARPATHS[@]}" "${APPDIR}/${JAR}" )
+done
+
+COLUMNS=80
+# get console width -- three ways to try, just in case (not needed for update)
+if command -v tput 2>&1 >/dev/null; then
+  COLUMNS=$(tput cols) 2>/dev/null
+elif command -v stty 2>&1 >/dev/null; then
+  COLUMNS=$(stty size | cut -d" " -f2) 2>/dev/null
+elif command -v resize 2>&1 >/dev/null; then
+  COLUMNS=$(resize -u | grep COLUMNS= | sed -e 's/.*=//;s/;//') 2>/dev/null
+fi
+JVMARGS=( "${JVMARGS[@]}" "-DCONSOLEWIDTH=${COLUMNS}" )
+JVMARGS=( "${JVMARGS[@]}" "-Dgetdownappdir=${APPDIR}" )
+JVMARGS=( "${JVMARGS[@]}" "-Dlauncher.script=${SCRIPT}" )
+JVMARGS=( "${JVMARGS[@]}" "-Dinstaller.appdir=${APPDIR}" )
+
+JAVAEXT=""
+# WINDOWS ONLY in Cygwin or Windows Subsystem for Linux (WSL)
+# change paths for Cygwin or WSL
 if [ "${ISMACOS}" != 1 ]; then # older macos doesn't like uname -o, best to avoid
-  if [ "$(uname -o)" = "Cygwin" ]; then
+  if [ "$(uname -o)" = "Cygwin" ]; then # Cygwin
   # CYGWIN
     CLASSPATH=$(cygpath -pw "${CLASSPATH}")
     # now for some arg paths fun. only translating paths starting with './', '../', '/' or '~'
@@ -128,7 +176,7 @@ if [ "${ISMACOS}" != 1 ]; then # older macos doesn't like uname -o, best to avoi
         ARGS=( "${ARGS[@]}" "${ARG}" )
       fi
     done
-  elif uname -r | grep -i microsoft | grep -i wsl >/dev/null; then
+  elif uname -r | grep -i microsoft | grep -i wsl >/dev/null; then # WSL
   # WSL
     CLASSPATH=""
     for JARPATH in "${JARPATHS[@]}"; do
@@ -146,32 +194,27 @@ if [ "${ISMACOS}" != 1 ]; then # older macos doesn't like uname -o, best to avoi
         ARGS=( "${ARGS[@]}" "${ARG}" )
       fi
     done
-    JAVA="${JAVA}.exe"
-    SYSJAVA="java.exe"
+    JAVAEXT=".exe"
+    JAVA="${JAVA}${JAVAEXT}"
+    SYSJAVA="java${JAVAEXT}"
   fi
 fi
 
-# get console width -- three ways to try, just in case
-if command -v tput 2>&1 >/dev/null; then
-  COLUMNS=$(tput cols) 2>/dev/null
-elif command -v stty 2>&1 >/dev/null; then
-  COLUMNS=$(stty size | cut -d" " -f2) 2>/dev/null
-elif command -v resize 2>&1 >/dev/null; then
-  COLUMNS=$(resize -u | grep COLUMNS= | sed -e 's/.*=//;s/;//') 2>/dev/null
+# look for bundled JRE. Might not be there if unix installer used in which case just invoke "java"
+if [ ! -z "${JAVABIN}" -a -e "${JAVABIN}/${NAME}${JAVAEXT}" ]; then
+  JAVA="${JAVABIN}/${NAME}${JAVAEXT}"
 fi
-JVMARGS=( "${JVMARGS[@]}" "-DCONSOLEWIDTH=${COLUMNS}" )
-JVMARGS=( "${JVMARGS[@]}" "-Dgetdownappdir=${APPDIR}" )
-
-# Is there a bundled Java?  If not just try one in the PATH (do need .exe in WSL)
-if [ \! -e "${JAVA}" ]; then
-  JAVA=$SYSJAVA
-  echo "Cannot find bundled java, using system ${JAVA} and hoping for the best!" >&2
+# If not just try one in the PATH (we need .exe in WSL, added above)
+if [ -z "${JAVABIN}" -o ! -e "${JAVA}" ]; then
+  JAVA="${SYSJAVA}"
+  echo "Cannot find bundled ${JAVA}, using system ${SYSJAVA} and hoping for the best!" >&2
 fi
 
+# This is just needed for display purposes
 function quotearray() {
   QUOTEDVALS=""
   for VAL in "${@}"; do
-    if [ \! "$QUOTEDVALS" = "" ]; then
+    if [ ! "$QUOTEDVALS" = "" ]; then
       QUOTEDVALS="${QUOTEDVALS} "
     fi
     QUOTEDVALS="${QUOTEDVALS}\"${VAL}\""
@@ -179,11 +222,12 @@ function quotearray() {
   echo $QUOTEDVALS
 }
 
+# for the debug command display
 JVMARGSSTR=$(quotearray "${JVMARGS[@]}")
 ARGSSTR=$(quotearray "${ARGS[@]}")
 
 if [ "${DEBUG}" = 1 ]; then
- echo Shell running: \""${JAVA}"\" ${JVMARGSSTR} -cp \""${CLASSPATH}"\" jalview.bin.Launcher ${ARGSSTR}
+ echo Shell running: \""${JAVA}"\" ${JVMARGSSTR} -cp \""${CLASSPATH}"\" \""${CLASS}"\" ${ARGSSTR} >&2
 fi
 
-"${JAVA}" "${JVMARGS[@]}" -cp "${CLASSPATH}" jalview.bin.Launcher "${ARGS[@]}"
+"${JAVA}" "${JVMARGS[@]}" -cp "${CLASSPATH}" "${CLASS}" "${ARGS[@]}"
diff --git a/utils/getdown/bin/run_other_script.ps1 b/utils/getdown/bin/run_other_script.ps1
new file mode 100755 (executable)
index 0000000..1c00beb
--- /dev/null
@@ -0,0 +1,49 @@
+#!/usr/bin/env pwsh
+
+$OTHERSCRIPT = "__OTHERSCRIPT__"
+
+# actual path of this script (which should be the getdown appdir/bin). Follow all symlinks.  Like GNU readlink -f
+function Readlink-f {
+  Param(
+    [Parameter(mandatory=$true, ValueFromPipeline=$true)]$Link,
+    [Parameter()]$iteration_count = 1
+  )
+  if ( $iteration_count -ge 100 ) {
+    Write-Error "Readlink-f iterated 100 times"
+    return $Link
+  }
+  if ( $Link -eq "" -or $Link -eq $null ) {
+    return $null
+  }
+  $path_components = @()
+  $dir = Get-Item $Link
+  while ( $dir -ne $null ) {
+    while ( $dir.Target -ne $null ) {
+      # [System.IO.Path]::Combine caters for a multitude of sins that it's almost impossible to deal with with Join-Path
+      $dir = Get-Item ([System.IO.Path]::GetFullPath( [System.IO.Path]::Combine( (Split-Path $dir -Parent), $dir.Target )))
+    }
+    $parent = Split-Path -Path $dir -Parent
+    $path_components = @( (Split-Path -Path $dir -Leaf) ) + $path_components
+    $dir = Readlink-f $parent ($iteration_count + 1)
+  }
+  $real = Get-Item "/"
+  foreach ( $component in $path_components) {
+    # [System.IO.Path]::Combine caters for a multitude of sins that it's almost impossible to deal with with Join-Path
+    $real = Get-Item ([System.IO.Path]::GetFullPath( [System.IO.Path]::Combine( $real, $component )))
+  }
+  $real
+}
+
+$CMDPATH = ( Get-Item $MyInvocation.MyCommand.Path )
+$SCRIPTPATH = Readlink-f -Link $CMDPATH
+$SCRIPTBIN = Split-Path -Path $SCRIPTPATH -Parent
+$SCRIPT = Join-Path $SCRIPTBIN -ChildPath $OTHERSCRIPT
+
+if ( $args.Count -eq 0 ) {
+  & $SCRIPT
+} else {
+  if ($args.Contains("--debug")) {
+    Write-Host "Running: $SCRIPT $args"
+  }
+  & $SCRIPT @args
+}
diff --git a/utils/getdown/bin/update.ps1 b/utils/getdown/bin/update.ps1
new file mode 100755 (executable)
index 0000000..82b74d4
--- /dev/null
@@ -0,0 +1,204 @@
+#!/usr/bin/env pwsh
+
+# setup for powershell version < 6.0
+[bool] $myIsWindows = 0
+[bool] $myIsMacOS = 0
+if ( $IsWindows -eq $null ) {
+  # for powershell version < 6.0 let's assume Windows
+  $myIsWindows = 1
+  $myIsMacOS = 0
+} else {
+  $myIsWindows = $IsWindows
+  $myIsMacOS = $IsMacOS
+}
+
+$USAGE = "Please use either --installation or --userspace."
+
+[bool] $debug = 0
+[bool] $userspace = 0
+[bool] $installation = 0
+Switch ($args) {
+  "--debug" {
+    $debug = 1
+    Continue
+  }
+  "--installation" {
+    $installation = 1
+    Continue
+  }
+  "--userspace" {
+    $userspace = 1
+    Continue
+  }
+  Default {
+    throw "Unknown option '$_'.  ${USAGE}."
+  }
+}
+
+if ( -not ( $userspace -or -$installation ) ) {
+  Write-Output "${USAGE}."
+  Exit
+}
+
+# actual path of this script (which should be the getdown appdir/bin). Follow all symlinks.  Like GNU readlink -f
+function Readlink-f {
+  Param(
+    [Parameter(mandatory=$true, ValueFromPipeline=$true)]$Link,
+    [Parameter()]$iteration_count = 1
+  )
+  if ( $iteration_count -ge 100 ) {
+    Write-Error "Readlink-f iterated 100 times"
+    return $Link
+  }
+  if ( $Link -eq "" -or $Link -eq $null ) {
+    return $null
+  }
+  $path_components = @()
+  $dir = Get-Item $Link
+  while ( $dir -ne $null ) {
+    while ( $dir.Target -ne $null ) {
+      # [System.IO.Path]::Combine caters for a multitude of sins that it's almost impossible to deal with with Join-Path
+      $dir = Get-Item ([System.IO.Path]::GetFullPath( [System.IO.Path]::Combine( (Split-Path $dir -Parent), $dir.Target )))
+    }
+    $parent = Split-Path -Path $dir -Parent
+    $path_components = @( (Split-Path -Path $dir -Leaf) ) + $path_components
+    $dir = Readlink-f $parent ($iteration_count + 1)
+  }
+  $real = Get-Item "/"
+  foreach ( $component in $path_components) {
+    # [System.IO.Path]::Combine caters for a multitude of sins that it's almost impossible to deal with with Join-Path
+    $real = Get-Item ([System.IO.Path]::GetFullPath( [System.IO.Path]::Combine( $real, $component )))
+  }
+  $real
+}
+
+# Avert problem with unix version of powershell and tell user the reason (Windows must always have .ps1 extension)
+if ( $MyInvocation.MyCommand.Path -eq $null ) {
+  throw "Script or link to script must have extension .ps1"
+}
+
+# args for the JVM
+$JVMARGS = @()
+
+$CMDPATH = ( Get-Item $MyInvocation.MyCommand.Path )
+$SCRIPTPATH = Readlink-f -Link $CMDPATH
+$SCRIPT = $SCRIPTPATH
+$DIR = Split-Path -Path $SCRIPTPATH -Parent
+$APPDIR = If ( ( Split-Path -Path $DIR -Leaf ) -eq "bin" ) { Split-Path -Path $DIR -Parent } Else { $DIR }
+$JAVAEXE = If ( $myIsWindows ) { "java.exe" } Else { "java" }
+if ( $myIsMacOS ) {
+  $JAVABIN = Join-Path -Path $APPDIR -ChildPath "jre/Contents/Home/bin"
+  $APPFOLDER = ( $APPDIR -Replace "\.app.*", "" ) + ".app"
+  $VMOPTIONS = Join-Path -Path $APPFOLDER -ChildPath "Contents/vmoptions.txt"
+} else {
+  $JAVABIN = Join-Path -Path $APPDIR -ChildPath "jre" | Join-Path -ChildPath "bin"
+  $APPFOLDER = $APPDIR
+  $VMOPTIONS = Join-Path -Path $APPDIR -ChildPath "jalviewg.vmoptions"
+}
+$JAVA = Join-Path -Path $JAVABIN -ChildPath $JAVAEXE
+
+$JVMARGS += "-Djava.awt.headless=true"
+if ( $myIsMacOS ) {
+  $JVMARGS += "-Dapple.awt.UIElement=true"
+}
+
+$GETDOWNLAUNCHERJAR = Join-Path -Path $APPDIR -ChildPath "getdown-launcher.jar"
+$GETDOWNCOREJAR = Join-Path -Path $APPDIR -ChildPath "resource" | Join-Path -ChildPath "getdown-core.jar"
+$CHANNELPROPS = Join-Path -Path $APPDIR -ChildPath "channel.props"
+$NAME = ( Select-String -Path "${CHANNELPROPS}" -Pattern "^app_name=(.*)$" ).Matches.Groups[1].Value
+$US_NAME = $NAME -Replace " ", "_"
+
+# getdown-launcher classpath
+if ( Test-Path -Path $GETDOWNLAUNCHERJAR ) {
+  $GDL_CLASSPATH = "${GETDOWNLAUNCHERJAR}"
+} else {
+  throw "Cannot find ${GETDOWNLAUNCHERJAR} to run update"
+}
+
+# getdown-core classpath
+if ( Test-Path -Path $GETDOWNCOREJAR ) {
+  $GDC_CLASSPATH = "${GETDOWNCOREJAR}"
+} else {
+  throw "Cannot find ${GETDOWNCOREJAR} to run update"
+}
+
+# USER space update
+if ( $userspace ) {
+  if ( Test-Path -Path $VMOPTIONS ) {
+    $LINENUM = 0
+    $lines = Get-Content -Path $VMOPTIONS
+    foreach ( $line in $lines ) {
+      # remove comments
+      $line -Replace "#.*", "" | Out-Null
+      # remove leading and trailing whitespace
+      $line -Replace "(^\s+|\s+$)", "" | Out-Null
+      $LINENUM++
+
+      if ( $line -Match "^-Dsetuserappdirpath=" ) {
+        $JVMARGS += $line
+      }
+      if ( $line -Match "^-Dnouserdefaultappdir=" ) {
+        $NOUSERUPDATEVALUE = ( $line -Replace ".*=", "" ).ToLower()
+        if ( $NOUSERUPDATEVALUE -eq "true" ) {
+          throw "Cannot perform manual user-space update when user-space updates are disabled.`nSee line ${LINENUM} (${line}) in file '${VMOPTIONS}'."
+        }
+        $JVMARGS += $line
+      }
+    }
+  } else {
+    echo "Could not find VMOPTIONS file '${VMOPTIONS}'"
+  }
+  $JVMARGS += "-Duserdefaultappdir=true"
+  $JVMARGS += "-Dnouserdefaultappdir=false"
+}
+
+# INSTALLATION update
+if ( $installation ) {
+  $JVMARGS += "-Duserdefaultappdir=false"
+  $JVMARGS += "-Dnouserdefaultappdir=true"
+  $JVMARGS += "-Dlauncher.appdir=${APPDIR}"
+  $JVMARGS += "-Dappdir=${APPDIR}"
+}
+
+# setting the appdir arg[0] and appid arg[1] to blank values
+# so appDir is determined by EnvConfig.getUserAppdir() or appdir property
+# and appId is passed as system property appid
+$GDL_ARGS = @( "", "" )
+
+# both USER and INSTALLATION updates
+$JVMARGS += "-Dinstaller.appdir=${APPDIR}"
+$JVMARGS += "-Dinstaller.application_folder=${US_NAME}"
+$JVMARGS += "-Dlauncher.script=${SCRIPT}"
+$JVMARGS += "-Dlauncher.update=true"
+$JVMARGS += "-Dchannel.app_name=${NAME}"
+$JVMARGS += "-Dappid=jalview"
+
+# IMPORTANT! tell getdown to update jalview withouth launching
+$JVMARGS += "-Dsilent=true"
+
+# add these Just In Case although jalview shouldn't be launched due to -Dsilent=true
+$GDL_ARGS += ( "--headless", "--quit", "--nojavaconsole", "--nosplash", "--nonews" )
+
+# look for bundled JRE. Might not be there if unix installer used in which case just invoke "java"
+if ( -not ( Test-Path -Path $JAVA ) ) {
+  Write-Host "Cannot find bundled ${JAVA}. Using system ${JAVAEXE} and hoping for the best!"
+  $JAVA = $JAVAEXE
+}
+
+# we should always have at least one JVMARGS and GDL_ARGS so this is okay
+$myJvmArgsString = '"' + $($JVMARGS -join '" "') + '"'
+$myGdlArgsString = '"' + $($GDL_ARGS -join '" "') + '"'
+
+# quote the args and the command (in case of spaces) with escape chars (`) and precede with & to indicate a command not string
+$COMMAND = "& `"${JAVA}`" ${myJvmArgsString} -cp `"${GDL_CLASSPATH}`" com.threerings.getdown.launcher.GetdownApp ${myGdlArgsString}"
+if ( $debug ) {
+  Write-Output "Shell running: ${COMMAND}"
+}
+Invoke-Expression -Command $COMMAND
+
+# move updated getdown-launcher.jar into place
+$COMMAND = "& `"${JAVA}`" ${myJvmArgsString} -cp `"${GDC_CLASSPATH}`" jalview.bin.GetdownLauncherUpdate"
+if ( $debug ) {
+  Write-Output "Shell running: ${COMMAND}"
+}
+Invoke-Expression -Command $COMMAND
diff --git a/utils/getdown/bin/update.sh b/utils/getdown/bin/update.sh
new file mode 100755 (executable)
index 0000000..03f369e
--- /dev/null
@@ -0,0 +1,253 @@
+#!/usr/bin/env bash
+
+declare -a ARGS=("${@}")
+
+ISMACOS=0
+if [ "$( uname -s )" = "Darwin" ]; then
+  ISMACOS=1
+fi
+
+USAGE="Please use either --installation or --userspace"
+
+DEBUG=0
+USERSPACE=0
+INSTALLATION=0
+DISABLEROOTCHECK=0
+for RAWARG in "${@}"; do
+  ARG="${RAWARG%%=*}"
+  case "${ARG}" in
+    --debug)
+      DEBUG=1
+      ;;
+    --installation)
+      INSTALLATION=1
+      ;;
+    --userspace)
+      USERSPACE=1
+      ;;
+    --norootcheck)
+      DISABLEROOTCHECK=1
+      ;;
+    *)
+      echo "Unknown option '${ARG}'.  ${USAGE}." >&2
+      exit 2
+  esac
+done
+
+if [ "${USERSPACE}${INSTALLATION}" = "00" ]; then
+  echo "${USAGE}."
+  exit
+fi
+
+# this whole next part is because there's no readlink -f in Darwin
+function readlinkf() {
+  FINDFILE="$1"
+  FILE="${FINDFILE}"
+  PREVFILE=""
+  C=0
+  MAX=100 # just in case we end up in a loop
+  FOUND=0
+  while [ "${C}" -lt "${MAX}" -a "${FILE}" != "${PREVFILE}" -a "${FOUND}" -ne 1 ]; do
+    PREVFILE="${FILE}"
+    FILE="$(readlink "${FILE}")"
+    if [ -z "${FILE}" ]; then
+      # the readlink is empty means we've arrived at the script, let's canonicalize with pwd
+      FILE="$(cd "$(dirname "${PREVFILE}")" &> /dev/null && pwd -P)"/"$(basename "${PREVFILE}")"
+      FOUND=1
+    elif [ "${FILE#/}" = "${FILE}" ]; then
+      # FILE is not an absolute path link, we need to add the relative path to the previous dir
+      FILE="$(dirname "${PREVFILE}")/${FILE}"
+    fi
+    C=$((C+1))
+  done
+  if [ "${FOUND}" -ne 1 ]; then
+    echo "Could not determine path to actual file '$(basename "${FINDFILE}")'" >&2
+    exit 1
+  fi
+  echo "${FILE}"
+}
+
+# args for the JVM
+declare -a JVMARGS=()
+
+# set vars for being inside the macos App Bundle
+if [ "${ISMACOS}" = 1 ]; then
+# MACOS ONLY
+  SCRIPT="$(readlinkf "$0")"
+  DIR="$(dirname "${SCRIPT}")"
+  APPDIR="${DIR%/bin}"
+  JAVABIN="${APPDIR}/jre/Contents/Home/bin"
+  # e.g. APPFOLDER=/Applications/Jalview.app
+  APPFOLDER="${APPDIR%%.app/*}.app"
+  VMOPTIONS="${APPFOLDER}/Contents/vmoptions.txt"
+else
+# NOT MACOS
+  SCRIPT="$(readlink -f "$0")"
+  DIR="$(dirname "${SCRIPT}")"
+  APPDIR="${DIR%/bin}"
+  JAVABIN="${APPDIR}/jre/bin"
+  # e.g. APPFOLDER=/opt/jalview
+  APPFOLDER="${APPDIR}"
+  VMOPTIONS="${APPDIR}/jalviewg.vmoptions"
+fi
+JAVA="${JAVABIN}/java"
+
+# headless java arguments
+JVMARGS=( "${JVMARGS[@]}" "-Djava.awt.headless=true" )
+if [ "${ISMACOS}" = 1 ]; then
+  JVMARGS=( "${JVMARGS[@]}" "-Dapple.awt.UIElement=true" )
+fi
+
+SYSJAVA=java
+GETDOWNLAUNCHERJAR="${APPDIR}/getdown-launcher.jar"
+GETDOWNCOREJAR="${APPDIR}/resource/getdown-core.jar"
+CHANNELPROPS="${APPDIR}/channel.props"
+NAME="$( grep app_name= "${CHANNELPROPS}" | cut -d= -f2 )"
+US_NAME="${NAME// /_}"
+
+GDL_CLASSPATH=""
+GDC_CLASSPATH=""
+# save an array of JAR paths in case we're in WSL (see later)
+declare -a GDL_JARPATHS=()
+declare -a GDC_JARPATHS=()
+declare -a GDL_ARGS=()
+
+# getdown-launcher classpath
+if [ -e "${GETDOWNLAUNCHERJAR}" ]; then
+  GDL_CLASSPATH="${GETDOWNLAUNCHERJAR}"
+  GDL_JARPATHS=( "${GDL_JARPATHS[@]}" "${GETDOWNLAUNCHERJAR}" )
+else
+  echo "Cannot find ${GETDOWNLAUNCHERJAR} to run update" >&2
+  exit 3
+fi
+
+# getdown-core classpath
+if [ -e "${GETDOWNCOREJAR}" ]; then
+  GDC_CLASSPATH="${GETDOWNCOREJAR}"
+  GDC_JARPATHS=( "${GDC_JARPATHS[@]}" "${GETDOWNCOREJAR}" )
+else
+  echo "Cannot find $( basename "${GETDOWNCOREJAR}" ) to run update" >&2
+  exit 4
+fi
+
+# USER space update
+if [ "${USERSPACE}" = 1 ]; then
+  if [ -e "${VMOPTIONS}" ]; then
+    LINENUM=0
+    while IFS= read -r line; do
+      # remove comments
+      line=${line%%#*}
+      # remove leading and trailing whitespace
+      line="${line%"${line##*[![:space:]]}"}"
+      line="${line#"${line%%[![:space:]]*}"}"
+      LINENUM=$((LINENUM+1))
+
+      # add settings for user appdir
+      if [ "${line}" != "${line#-Dsetuserappdirpath=}" ]; then # starts with -Dsetuserappdirpath=
+        JVMARGS=( "${JVMARGS[@]}" "${line}" )
+      fi
+      if [ "${line}" != "${line#-Dnouserdefaultappdir=}" ]; then # starts with -Dnouserdefaultappdir=
+        # don't perform user update if user updates are disabled
+        NOUSERUPDATEVALUE="$(echo "${line#-Dnouserdefaultappdir=}" | tr '[:upper:]' '[:lower:]')"
+        if [ "${NOUSERUPDATEVALUE}" = "true" ]; then
+          echo "Cannot perform manual user-space update when user-space updates are disabled." >&2
+          echo "See line ${LINENUM} (${line}) in file '${VMOPTIONS}'." >&2
+          exit 5
+        fi
+        JVMARGS=( "${JVMARGS[@]}" "${line}" )
+      fi
+    done < "${VMOPTIONS}"
+  fi
+  JVMARGS=( "${JVMARGS[@]}" "-Duserdefaultappdir=true" )
+  JVMARGS=( "${JVMARGS[@]}" "-Dnouserdefaultappdir=false" )
+fi
+
+# INSTALLATION update
+if [ "${INSTALLATION}" = 1 ]; then
+  # root permissions check
+  if [ "${DISABLEROOTCHECK}" != 1 -a "${EUID}" != 0 ]; then
+    echo "--installation updates should be run with root permissions, or disable this root check with --norootcheck" >&2
+    exit 6
+  fi
+  JVMARGS=( "${JVMARGS[@]}" "-Duserdefaultappdir=false" )
+  JVMARGS=( "${JVMARGS[@]}" "-Dnouserdefaultappdir=true" )
+  JVMARGS=( "${JVMARGS[@]}" "-Dlauncher.appdir=${APPDIR}" )
+  JVMARGS=( "${JVMARGS[@]}" "-Dappdir=${APPDIR}" )
+fi
+
+# setting the appdir arg[0] and appid arg[1] to blank values
+# so appDir is determined by EnvConfig.getUserAppdir() or appdir property
+# and appId is passed as system property appid
+GDL_ARGS=( "" "" )
+
+# both USER and INSTALLATION updates
+JVMARGS=( "${JVMARGS[@]}" "-Dinstaller.appdir=${APPDIR}" )
+JVMARGS=( "${JVMARGS[@]}" "-Dinstaller.application_folder=${US_NAME}" )
+JVMARGS=( "${JVMARGS[@]}" "-Dlauncher.script=${SCRIPT}" )
+JVMARGS=( "${JVMARGS[@]}" "-Dlauncher.update=true" )
+JVMARGS=( "${JVMARGS[@]}" "-Dchannel.app_name=${NAME}" )
+JVMARGS=( "${JVMARGS[@]}" "-Dappid=jalview" )
+
+# IMPORTANT! tell getdown to update jalview withouth launching
+JVMARGS=( "${JVMARGS[@]}" "-Dsilent=true" )
+
+# add these Just In Case although jalview shouldn't be launched due to -Dsilent=true
+GDL_ARGS=( "${GDL_ARGS[@]}" "--headless" "--quit" "--nojavaconsole" "--nosplash" "--nonews" )
+
+# WINDOWS ONLY in Cygwin or Windows Subsystem for Linux (WSL)
+# change paths for Cygwin or WSL
+if [ "${ISMACOS}" != 1 ]; then # older macos doesn't like uname -o, best to avoid
+  if [ "$(uname -o)" = "Cygwin" ]; then
+    # CYGWIN
+    GDL_CLASSPATH=$(cygpath -pw "${GDL_CLASSPATH}")
+    GDC_CLASSPATH=$(cygpath -pw "${GDC_CLASSPATH}")
+  elif uname -r | grep -i microsoft | grep -i wsl >/dev/null; then
+    # WSL
+    GDL_CLASSPATH=""
+    for JARPATH in "${GDL_JARPATHS[@]}"; do
+      [ -n "${GDL_CLASSPATH}" ] && GDL_CLASSPATH="${GDL_CLASSPATH};"
+      GDL_CLASSPATH="${GDL_CLASSPATH}$(wslpath -aw "${JARPATH}")"
+    done
+    GDC_CLASSPATH=""
+    for JARPATH in "${GDC_JARPATHS[@]}"; do
+      [ -n "${GDC_CLASSPATH}" ] && GDC_CLASSPATH="${GDC_CLASSPATH};"
+      GDC_CLASSPATH="${GDC_CLASSPATH}$(wslpath -aw "${JARPATH}")"
+    done
+    JAVA="${JAVA}.exe"
+    SYSJAVA="java.exe"
+  fi
+fi
+
+# Is there a bundled Java?  If not just try one in the PATH (we need .exe in WSL, added above)
+if [ \! -e "${JAVA}" ]; then
+  JAVA=$SYSJAVA
+  echo "Cannot find bundled ${JAVA}, using system ${SYSJAVA} and hoping for the best!" >&2
+fi
+
+# This is just needed for display purposes
+function quotearray() {
+  QUOTEDVALS=""
+  for VAL in "${@}"; do
+    if [ \! "$QUOTEDVALS" = "" ]; then
+      QUOTEDVALS="${QUOTEDVALS} "
+    fi
+    QUOTEDVALS="${QUOTEDVALS}\"${VAL}\""
+  done
+  echo $QUOTEDVALS
+}
+
+JVMARGSSTR=$(quotearray "${JVMARGS[@]}")
+ARGSSTR=$(quotearray "${GDL_ARGS[@]}")
+
+if [ "${DEBUG}" = 1 ]; then
+ echo Shell running: \""${JAVA}"\" ${JVMARGSSTR} -cp \""${GDL_CLASSPATH}"\" com.threerings.getdown.launcher.GetdownApp ${ARGSSTR} >&2
+fi
+
+"${JAVA}" "${JVMARGS[@]}" -cp "${GDL_CLASSPATH}" com.threerings.getdown.launcher.GetdownApp "${GDL_ARGS[@]}"
+
+# move updated getdown-launcher.jar into place
+if [ "${DEBUG}" = 1 ]; then
+  echo Shell running: \""${JAVA}"\" ${JVMARGSSTR} -cp \""${GDC_CLASSPATH}"\" jalview.bin.GetdownLauncherUpdate >&2
+fi
+
+"${JAVA}" "${JVMARGS[@]}" -cp "${GDC_CLASSPATH}" jalview.bin.GetdownLauncherUpdate
diff --git a/utils/install4j/default.vmoptions b/utils/install4j/default.vmoptions
new file mode 100644 (file)
index 0000000..9989aa7
--- /dev/null
@@ -0,0 +1,21 @@
+# Enter one VM parameter per line
+# For example, to adjust the maximum memory usage to 512 MB, uncomment the following line:
+# -Xmx512m
+# To include another file, uncomment the following line:
+# -include-options [path to other .vmoption file]
+
+# Jalview specific options below
+
+# Jalview options added by __INSTALLERFILENAME__ at __INSTALLDATETIME__
+# 
+# Uncomment the following line to disable user-space updates
+#-Dnouserdefaultappdir=true
+# 
+# Uncomment the below line to set a custom path for user-space updates -- use with caution.
+# A leading ~/ or %h anywhere will be substituted with the user's home path, and %u by the username.
+# If not set, the default is ${installer:userDefaultAppdirBase} for ${installer:osName}
+#-Dsetuserappdirpath=/tmp/jalview/%u
+# 
+# Uncomment the following line to also disable all updates
+#-Dsilent=noupdate 
+
index 4a87364..7364320 100644 (file)
@@ -1,5 +1,5 @@
 <!-- Jalview (.jvp) BEGIN -->
-                  <action name="Jalview (.jvp) message" id="10000" customizedId="Jalview-jvp-10000-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Jalview (.jvp) message" id="10000" customizedId="FA_MESSAGE-Jalview" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">Jalview (.jvp)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
@@ -8,13 +8,13 @@
                     </serializedBean>
                   </action>
 
-                  <action name="Jalview (.jvp) progress bar 0" id="10001" customizedId="Jalview-jvp-10001-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Jalview (.jvp) progress bar 0" id="10001" customizedId="FA_PROGRESSBAR-Jalview" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="percentValue" type="int" value="0" />
                     </serializedBean>
                   </action>
 
-                  <action name="Jalview (.jvp) file association" id="10002" customizedId="Jalview-jvp-10002-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .jvp file association">
+                  <action name="Jalview (.jvp) file association" id="10002" customizedId="FA_FILEASSOCIATION-Jalview-true" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .jvp file association">
                     <serializedBean>
                       <property name="description" type="string">Jalview File</property>
                       <property name="extension" type="string">jvp</property>
@@ -44,7 +44,7 @@
 <!-- END -->
 
 <!-- Jalview Launch (.jvl) BEGIN -->
-                  <action name="Jalview Launch (.jvl) message" id="10003" customizedId="Jalview Launch-jvl-10003-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Jalview Launch (.jvl) message" id="10003" customizedId="FA_MESSAGE-Jalview Launch" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">Jalview Launch (.jvl)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="Jalview Launch (.jvl) progress bar 3" id="10004" customizedId="Jalview Launch-jvl-10004-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Jalview Launch (.jvl) progress bar 3" id="10004" customizedId="FA_PROGRESSBAR-Jalview Launch" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="percentValue" type="int" value="3" />
                     </serializedBean>
                   </action>
 
-                  <action name="Jalview Launch (.jvl) file association" id="10005" customizedId="Jalview Launch-jvl-10005-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .jvl file association">
+                  <action name="Jalview Launch (.jvl) file association" id="10005" customizedId="FA_FILEASSOCIATION-Jalview Launch-true" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .jvl file association">
                     <serializedBean>
                       <property name="description" type="string">Jalview Launch File</property>
                       <property name="extension" type="string">jvl</property>
@@ -89,7 +89,7 @@
 <!-- END -->
 
 <!-- CIF (.cif) BEGIN -->
-                  <action name="CIF (.cif) message" id="10006" customizedId="CIF-cif-10006-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="CIF (.cif) message" id="10006" customizedId="FA_MESSAGE-CIF" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">CIF (.cif)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="CIF (.cif) progress bar 7" id="10007" customizedId="CIF-cif-10007-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="CIF (.cif) progress bar 7" id="10007" customizedId="FA_PROGRESSBAR-CIF" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="percentValue" type="int" value="7" />
                     </serializedBean>
                   </action>
 
-                  <action name="CIF (.cif) file association" id="10008" customizedId="CIF-cif-10008-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .cif file association">
+                  <action name="CIF (.cif) file association" id="10008" customizedId="FA_FILEASSOCIATION-CIF-false" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .cif file association">
                     <serializedBean>
                       <property name="description" type="string">CIF File</property>
                       <property name="extension" type="string">cif</property>
 <!-- END -->
 
 <!-- mmCIF (.mcif, .mmcif) BEGIN -->
-                  <action name="mmCIF (.mcif, .mmcif) message" id="10009" customizedId="mmCIF-mcif,mmcif-10009-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="mmCIF (.mcif, .mmcif) message" id="10009" customizedId="FA_MESSAGE-mmCIF" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">mmCIF (.mcif, .mmcif)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="mmCIF (.mcif, .mmcif) progress bar 11" id="10010" customizedId="mmCIF-mcif,mmcif-10010-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="mmCIF (.mcif, .mmcif) progress bar 11" id="10010" customizedId="FA_PROGRESSBAR-mmCIF" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="percentValue" type="int" value="11" />
                     </serializedBean>
                   </action>
 
-                  <action name="mmCIF (.mcif, .mmcif) file association" id="10011" customizedId="mmCIF-mcif,mmcif-10011-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .mcif,mmcif file association">
+                  <action name="mmCIF (.mcif, .mmcif) file association" id="10011" customizedId="FA_FILEASSOCIATION-mmCIF-false" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .mcif,mmcif file association">
                     <serializedBean>
                       <property name="description" type="string">mmCIF File</property>
                       <property name="extension" type="string">mcif,mmcif</property>
 <!-- END -->
 
 <!-- PDB (.ent, .pdb) BEGIN -->
-                  <action name="PDB (.ent, .pdb) message" id="10012" customizedId="PDB-ent,pdb-10012-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="PDB (.ent, .pdb) message" id="10012" customizedId="FA_MESSAGE-PDB" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">PDB (.ent, .pdb)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="PDB (.ent, .pdb) progress bar 15" id="10013" customizedId="PDB-ent,pdb-10013-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="PDB (.ent, .pdb) progress bar 15" id="10013" customizedId="FA_PROGRESSBAR-PDB" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="percentValue" type="int" value="15" />
                     </serializedBean>
                   </action>
 
-                  <action name="PDB (.ent, .pdb) file association" id="10014" customizedId="PDB-ent,pdb-10014-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .ent,pdb file association">
+                  <action name="PDB (.ent, .pdb) file association" id="10014" customizedId="FA_FILEASSOCIATION-PDB-false" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .ent,pdb file association">
                     <serializedBean>
                       <property name="description" type="string">PDB File</property>
                       <property name="extension" type="string">ent,pdb</property>
 <!-- END -->
 
 <!-- AMSA (.amsa) BEGIN -->
-                  <action name="AMSA (.amsa) message" id="10015" customizedId="AMSA-amsa-10015-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="AMSA (.amsa) message" id="10015" customizedId="FA_MESSAGE-AMSA" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">AMSA (.amsa)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="AMSA (.amsa) progress bar 19" id="10016" customizedId="AMSA-amsa-10016-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="AMSA (.amsa) progress bar 19" id="10016" customizedId="FA_PROGRESSBAR-AMSA" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="percentValue" type="int" value="19" />
                     </serializedBean>
                   </action>
 
-                  <action name="AMSA (.amsa) file association" id="10017" customizedId="AMSA-amsa-10017-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .amsa file association">
+                  <action name="AMSA (.amsa) file association" id="10017" customizedId="FA_FILEASSOCIATION-AMSA-true" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .amsa file association">
                     <serializedBean>
                       <property name="description" type="string">AMSA File</property>
                       <property name="extension" type="string">amsa</property>
 <!-- END -->
 
 <!-- Jalview Annotations (.annotations, .jvannotations) BEGIN -->
-                  <action name="Jalview Annotations (.annotations, .jvannotations) message" id="10018" customizedId="Jalview Annotations-annotations,jvannotations-10018-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Jalview Annotations (.annotations, .jvannotations) message" id="10018" customizedId="FA_MESSAGE-Jalview Annotations" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">Jalview Annotations (.annotations, .jvannotations)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="Jalview Annotations (.annotations, .jvannotations) progress bar 23" id="10019" customizedId="Jalview Annotations-annotations,jvannotations-10019-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Jalview Annotations (.annotations, .jvannotations) progress bar 23" id="10019" customizedId="FA_PROGRESSBAR-Jalview Annotations" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="percentValue" type="int" value="23" />
                     </serializedBean>
                   </action>
 
-                  <action name="Jalview Annotations (.annotations, .jvannotations) file association" id="10020" customizedId="Jalview Annotations-annotations,jvannotations-10020-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .annotations,jvannotations file association">
+                  <action name="Jalview Annotations (.annotations, .jvannotations) file association" id="10020" customizedId="FA_FILEASSOCIATION-Jalview Annotations-true" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .annotations,jvannotations file association">
                     <serializedBean>
                       <property name="description" type="string">Jalview Annotations File</property>
                       <property name="extension" type="string">annotations,jvannotations</property>
 <!-- END -->
 
 <!-- BioJSON (.biojson) BEGIN -->
-                  <action name="BioJSON (.biojson) message" id="10021" customizedId="BioJSON-biojson-10021-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="BioJSON (.biojson) message" id="10021" customizedId="FA_MESSAGE-BioJSON" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">BioJSON (.biojson)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="BioJSON (.biojson) progress bar 26" id="10022" customizedId="BioJSON-biojson-10022-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="BioJSON (.biojson) progress bar 26" id="10022" customizedId="FA_PROGRESSBAR-BioJSON" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="percentValue" type="int" value="26" />
                     </serializedBean>
                   </action>
 
-                  <action name="BioJSON (.biojson) file association" id="10023" customizedId="BioJSON-biojson-10023-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .biojson file association">
+                  <action name="BioJSON (.biojson) file association" id="10023" customizedId="FA_FILEASSOCIATION-BioJSON-true" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .biojson file association">
                     <serializedBean>
                       <property name="description" type="string">BioJSON File</property>
                       <property name="extension" type="string">biojson</property>
 <!-- END -->
 
 <!-- BLC (.blc) BEGIN -->
-                  <action name="BLC (.blc) message" id="10024" customizedId="BLC-blc-10024-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="BLC (.blc) message" id="10024" customizedId="FA_MESSAGE-BLC" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">BLC (.blc)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="BLC (.blc) progress bar 30" id="10025" customizedId="BLC-blc-10025-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="BLC (.blc) progress bar 30" id="10025" customizedId="FA_PROGRESSBAR-BLC" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="percentValue" type="int" value="30" />
                     </serializedBean>
                   </action>
 
-                  <action name="BLC (.blc) file association" id="10026" customizedId="BLC-blc-10026-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .blc file association">
+                  <action name="BLC (.blc) file association" id="10026" customizedId="FA_FILEASSOCIATION-BLC-true" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .blc file association">
                     <serializedBean>
                       <property name="description" type="string">BLC File</property>
                       <property name="extension" type="string">blc</property>
 <!-- END -->
 
 <!-- Clustal (.aln) BEGIN -->
-                  <action name="Clustal (.aln) message" id="10027" customizedId="Clustal-aln-10027-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Clustal (.aln) message" id="10027" customizedId="FA_MESSAGE-Clustal" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">Clustal (.aln)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="Clustal (.aln) progress bar 34" id="10028" customizedId="Clustal-aln-10028-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Clustal (.aln) progress bar 34" id="10028" customizedId="FA_PROGRESSBAR-Clustal" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="percentValue" type="int" value="34" />
                     </serializedBean>
                   </action>
 
-                  <action name="Clustal (.aln) file association" id="10029" customizedId="Clustal-aln-10029-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .aln file association">
+                  <action name="Clustal (.aln) file association" id="10029" customizedId="FA_FILEASSOCIATION-Clustal-true" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .aln file association">
                     <serializedBean>
                       <property name="description" type="string">Clustal File</property>
                       <property name="extension" type="string">aln</property>
 <!-- END -->
 
 <!-- Fasta (.fa, .fasta) BEGIN -->
-                  <action name="Fasta (.fa, .fasta) message" id="10030" customizedId="Fasta-fa,fasta-10030-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Fasta (.fa, .fasta) message" id="10030" customizedId="FA_MESSAGE-Fasta" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">Fasta (.fa, .fasta)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="Fasta (.fa, .fasta) progress bar 38" id="10031" customizedId="Fasta-fa,fasta-10031-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Fasta (.fa, .fasta) progress bar 38" id="10031" customizedId="FA_PROGRESSBAR-Fasta" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="percentValue" type="int" value="38" />
                     </serializedBean>
                   </action>
 
-                  <action name="Fasta (.fa, .fasta) file association" id="10032" customizedId="Fasta-fa,fasta-10032-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .fa,fasta file association">
+                  <action name="Fasta (.fa, .fasta) file association" id="10032" customizedId="FA_FILEASSOCIATION-Fasta-true" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .fa,fasta file association">
                     <serializedBean>
                       <property name="description" type="string">Fasta File</property>
                       <property name="extension" type="string">fa,fasta</property>
 <!-- END -->
 
 <!-- Jalview Features (.features, .jvfeatures) BEGIN -->
-                  <action name="Jalview Features (.features, .jvfeatures) message" id="10033" customizedId="Jalview Features-features,jvfeatures-10033-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Jalview Features (.features, .jvfeatures) message" id="10033" customizedId="FA_MESSAGE-Jalview Features" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">Jalview Features (.features, .jvfeatures)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="Jalview Features (.features, .jvfeatures) progress bar 42" id="10034" customizedId="Jalview Features-features,jvfeatures-10034-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Jalview Features (.features, .jvfeatures) progress bar 42" id="10034" customizedId="FA_PROGRESSBAR-Jalview Features" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="percentValue" type="int" value="42" />
                     </serializedBean>
                   </action>
 
-                  <action name="Jalview Features (.features, .jvfeatures) file association" id="10035" customizedId="Jalview Features-features,jvfeatures-10035-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .features,jvfeatures file association">
+                  <action name="Jalview Features (.features, .jvfeatures) file association" id="10035" customizedId="FA_FILEASSOCIATION-Jalview Features-true" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .features,jvfeatures file association">
                     <serializedBean>
                       <property name="description" type="string">Jalview Features File</property>
                       <property name="extension" type="string">features,jvfeatures</property>
 <!-- END -->
 
 <!-- Jalview Feature Settings File (.fc) BEGIN -->
-                  <action name="Jalview Feature Settings File (.fc) message" id="10036" customizedId="Jalview Feature Settings File-fc-10036-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Jalview Feature Settings File (.fc) message" id="10036" customizedId="FA_MESSAGE-Jalview Feature Settings File" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">Jalview Feature Settings File (.fc)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="Jalview Feature Settings File (.fc) progress bar 46" id="10037" customizedId="Jalview Feature Settings File-fc-10037-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Jalview Feature Settings File (.fc) progress bar 46" id="10037" customizedId="FA_PROGRESSBAR-Jalview Feature Settings File" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="percentValue" type="int" value="46" />
                     </serializedBean>
                   </action>
 
-                  <action name="Jalview Feature Settings File (.fc) file association" id="10038" customizedId="Jalview Feature Settings File-fc-10038-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .fc file association">
+                  <action name="Jalview Feature Settings File (.fc) file association" id="10038" customizedId="FA_FILEASSOCIATION-Jalview Feature Settings File-true" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .fc file association">
                     <serializedBean>
                       <property name="description" type="string">Jalview Feature Settings File File</property>
                       <property name="extension" type="string">fc</property>
 <!-- END -->
 
 <!-- GenBank Flatfile (.gb, .gbk) BEGIN -->
-                  <action name="GenBank Flatfile (.gb, .gbk) message" id="10039" customizedId="GenBank Flatfile-gb,gbk-10039-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="GenBank Flatfile (.gb, .gbk) message" id="10039" customizedId="FA_MESSAGE-GenBank Flatfile" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">GenBank Flatfile (.gb, .gbk)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="GenBank Flatfile (.gb, .gbk) progress bar 50" id="10040" customizedId="GenBank Flatfile-gb,gbk-10040-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="GenBank Flatfile (.gb, .gbk) progress bar 50" id="10040" customizedId="FA_PROGRESSBAR-GenBank Flatfile" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="percentValue" type="int" value="50" />
                     </serializedBean>
                   </action>
 
-                  <action name="GenBank Flatfile (.gb, .gbk) file association" id="10041" customizedId="GenBank Flatfile-gb,gbk-10041-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .gb,gbk file association">
+                  <action name="GenBank Flatfile (.gb, .gbk) file association" id="10041" customizedId="FA_FILEASSOCIATION-GenBank Flatfile-true" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .gb,gbk file association">
                     <serializedBean>
                       <property name="description" type="string">GenBank Flatfile File</property>
                       <property name="extension" type="string">gb,gbk</property>
 <!-- END -->
 
 <!-- Generic Features Format v2 (.gff2) BEGIN -->
-                  <action name="Generic Features Format v2 (.gff2) message" id="10042" customizedId="Generic Features Format v2-gff2-10042-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Generic Features Format v2 (.gff2) message" id="10042" customizedId="FA_MESSAGE-Generic Features Format v2" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">Generic Features Format v2 (.gff2)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="Generic Features Format v2 (.gff2) progress bar 53" id="10043" customizedId="Generic Features Format v2-gff2-10043-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Generic Features Format v2 (.gff2) progress bar 53" id="10043" customizedId="FA_PROGRESSBAR-Generic Features Format v2" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="percentValue" type="int" value="53" />
                     </serializedBean>
                   </action>
 
-                  <action name="Generic Features Format v2 (.gff2) file association" id="10044" customizedId="Generic Features Format v2-gff2-10044-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .gff2 file association">
+                  <action name="Generic Features Format v2 (.gff2) file association" id="10044" customizedId="FA_FILEASSOCIATION-Generic Features Format v2-true" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .gff2 file association">
                     <serializedBean>
                       <property name="description" type="string">Generic Features Format v2 File</property>
                       <property name="extension" type="string">gff2</property>
 <!-- END -->
 
 <!-- Generic Features Format v3 (.gff3) BEGIN -->
-                  <action name="Generic Features Format v3 (.gff3) message" id="10045" customizedId="Generic Features Format v3-gff3-10045-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Generic Features Format v3 (.gff3) message" id="10045" customizedId="FA_MESSAGE-Generic Features Format v3" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">Generic Features Format v3 (.gff3)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="Generic Features Format v3 (.gff3) progress bar 57" id="10046" customizedId="Generic Features Format v3-gff3-10046-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Generic Features Format v3 (.gff3) progress bar 57" id="10046" customizedId="FA_PROGRESSBAR-Generic Features Format v3" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="percentValue" type="int" value="57" />
                     </serializedBean>
                   </action>
 
-                  <action name="Generic Features Format v3 (.gff3) file association" id="10047" customizedId="Generic Features Format v3-gff3-10047-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .gff3 file association">
+                  <action name="Generic Features Format v3 (.gff3) file association" id="10047" customizedId="FA_FILEASSOCIATION-Generic Features Format v3-true" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .gff3 file association">
                     <serializedBean>
                       <property name="description" type="string">Generic Features Format v3 File</property>
                       <property name="extension" type="string">gff3</property>
 <!-- END -->
 
 <!-- JnetFile (.concise, .jnet) BEGIN -->
-                  <action name="JnetFile (.concise, .jnet) message" id="10048" customizedId="JnetFile-concise,jnet-10048-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="JnetFile (.concise, .jnet) message" id="10048" customizedId="FA_MESSAGE-JnetFile" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">JnetFile (.concise, .jnet)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="JnetFile (.concise, .jnet) progress bar 61" id="10049" customizedId="JnetFile-concise,jnet-10049-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="JnetFile (.concise, .jnet) progress bar 61" id="10049" customizedId="FA_PROGRESSBAR-JnetFile" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="percentValue" type="int" value="61" />
                     </serializedBean>
                   </action>
 
-                  <action name="JnetFile (.concise, .jnet) file association" id="10050" customizedId="JnetFile-concise,jnet-10050-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .concise,jnet file association">
+                  <action name="JnetFile (.concise, .jnet) file association" id="10050" customizedId="FA_FILEASSOCIATION-JnetFile-true" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .concise,jnet file association">
                     <serializedBean>
                       <property name="description" type="string">JnetFile File</property>
                       <property name="extension" type="string">concise,jnet</property>
 <!-- END -->
 
 <!-- MSF (.msf) BEGIN -->
-                  <action name="MSF (.msf) message" id="10051" customizedId="MSF-msf-10051-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="MSF (.msf) message" id="10051" customizedId="FA_MESSAGE-MSF" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">MSF (.msf)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="MSF (.msf) progress bar 65" id="10052" customizedId="MSF-msf-10052-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="MSF (.msf) progress bar 65" id="10052" customizedId="FA_PROGRESSBAR-MSF" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="percentValue" type="int" value="65" />
                     </serializedBean>
                   </action>
 
-                  <action name="MSF (.msf) file association" id="10053" customizedId="MSF-msf-10053-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .msf file association">
+                  <action name="MSF (.msf) file association" id="10053" customizedId="FA_FILEASSOCIATION-MSF-true" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .msf file association">
                     <serializedBean>
                       <property name="description" type="string">MSF File</property>
                       <property name="extension" type="string">msf</property>
 <!-- END -->
 
 <!-- PFAM (.pfam) BEGIN -->
-                  <action name="PFAM (.pfam) message" id="10054" customizedId="PFAM-pfam-10054-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="PFAM (.pfam) message" id="10054" customizedId="FA_MESSAGE-PFAM" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">PFAM (.pfam)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="PFAM (.pfam) progress bar 69" id="10055" customizedId="PFAM-pfam-10055-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="PFAM (.pfam) progress bar 69" id="10055" customizedId="FA_PROGRESSBAR-PFAM" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="percentValue" type="int" value="69" />
                     </serializedBean>
                   </action>
 
-                  <action name="PFAM (.pfam) file association" id="10056" customizedId="PFAM-pfam-10056-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .pfam file association">
+                  <action name="PFAM (.pfam) file association" id="10056" customizedId="FA_FILEASSOCIATION-PFAM-true" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .pfam file association">
                     <serializedBean>
                       <property name="description" type="string">PFAM File</property>
                       <property name="extension" type="string">pfam</property>
 <!-- END -->
 
 <!-- PHYLIP (.phy) BEGIN -->
-                  <action name="PHYLIP (.phy) message" id="10057" customizedId="PHYLIP-phy-10057-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="PHYLIP (.phy) message" id="10057" customizedId="FA_MESSAGE-PHYLIP" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">PHYLIP (.phy)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="PHYLIP (.phy) progress bar 73" id="10058" customizedId="PHYLIP-phy-10058-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="PHYLIP (.phy) progress bar 73" id="10058" customizedId="FA_PROGRESSBAR-PHYLIP" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="percentValue" type="int" value="73" />
                     </serializedBean>
                   </action>
 
-                  <action name="PHYLIP (.phy) file association" id="10059" customizedId="PHYLIP-phy-10059-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .phy file association">
+                  <action name="PHYLIP (.phy) file association" id="10059" customizedId="FA_FILEASSOCIATION-PHYLIP-true" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .phy file association">
                     <serializedBean>
                       <property name="description" type="string">PHYLIP File</property>
                       <property name="extension" type="string">phy</property>
 <!-- END -->
 
 <!-- PileUp (.pileup) BEGIN -->
-                  <action name="PileUp (.pileup) message" id="10060" customizedId="PileUp-pileup-10060-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="PileUp (.pileup) message" id="10060" customizedId="FA_MESSAGE-PileUp" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">PileUp (.pileup)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="PileUp (.pileup) progress bar 76" id="10061" customizedId="PileUp-pileup-10061-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="PileUp (.pileup) progress bar 76" id="10061" customizedId="FA_PROGRESSBAR-PileUp" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="percentValue" type="int" value="76" />
                     </serializedBean>
                   </action>
 
-                  <action name="PileUp (.pileup) file association" id="10062" customizedId="PileUp-pileup-10062-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .pileup file association">
+                  <action name="PileUp (.pileup) file association" id="10062" customizedId="FA_FILEASSOCIATION-PileUp-true" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .pileup file association">
                     <serializedBean>
                       <property name="description" type="string">PileUp File</property>
                       <property name="extension" type="string">pileup</property>
 <!-- END -->
 
 <!-- PIR (.pir) BEGIN -->
-                  <action name="PIR (.pir) message" id="10063" customizedId="PIR-pir-10063-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="PIR (.pir) message" id="10063" customizedId="FA_MESSAGE-PIR" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">PIR (.pir)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="PIR (.pir) progress bar 80" id="10064" customizedId="PIR-pir-10064-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="PIR (.pir) progress bar 80" id="10064" customizedId="FA_PROGRESSBAR-PIR" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="percentValue" type="int" value="80" />
                     </serializedBean>
                   </action>
 
-                  <action name="PIR (.pir) file association" id="10065" customizedId="PIR-pir-10065-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .pir file association">
+                  <action name="PIR (.pir) file association" id="10065" customizedId="FA_FILEASSOCIATION-PIR-true" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .pir file association">
                     <serializedBean>
                       <property name="description" type="string">PIR File</property>
                       <property name="extension" type="string">pir</property>
 <!-- END -->
 
 <!-- RNAML (.rnaml) BEGIN -->
-                  <action name="RNAML (.rnaml) message" id="10066" customizedId="RNAML-rnaml-10066-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="RNAML (.rnaml) message" id="10066" customizedId="FA_MESSAGE-RNAML" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">RNAML (.rnaml)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="RNAML (.rnaml) progress bar 84" id="10067" customizedId="RNAML-rnaml-10067-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="RNAML (.rnaml) progress bar 84" id="10067" customizedId="FA_PROGRESSBAR-RNAML" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="percentValue" type="int" value="84" />
                     </serializedBean>
                   </action>
 
-                  <action name="RNAML (.rnaml) file association" id="10068" customizedId="RNAML-rnaml-10068-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .rnaml file association">
+                  <action name="RNAML (.rnaml) file association" id="10068" customizedId="FA_FILEASSOCIATION-RNAML-true" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .rnaml file association">
                     <serializedBean>
                       <property name="description" type="string">RNAML File</property>
                       <property name="extension" type="string">rnaml</property>
 <!-- END -->
 
 <!-- Substitution Matrix (.mat) BEGIN -->
-                  <action name="Substitution Matrix (.mat) message" id="10069" customizedId="Substitution Matrix-mat-10069-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Substitution Matrix (.mat) message" id="10069" customizedId="FA_MESSAGE-Substitution Matrix" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">Substitution Matrix (.mat)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="Substitution Matrix (.mat) progress bar 88" id="10070" customizedId="Substitution Matrix-mat-10070-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Substitution Matrix (.mat) progress bar 88" id="10070" customizedId="FA_PROGRESSBAR-Substitution Matrix" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="percentValue" type="int" value="88" />
                     </serializedBean>
                   </action>
 
-                  <action name="Substitution Matrix (.mat) file association" id="10071" customizedId="Substitution Matrix-mat-10071-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .mat file association">
+                  <action name="Substitution Matrix (.mat) file association" id="10071" customizedId="FA_FILEASSOCIATION-Substitution Matrix-true" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .mat file association">
                     <serializedBean>
                       <property name="description" type="string">Substitution Matrix File</property>
                       <property name="extension" type="string">mat</property>
 <!-- END -->
 
 <!-- Stockholm (.stk, .sto) BEGIN -->
-                  <action name="Stockholm (.stk, .sto) message" id="10072" customizedId="Stockholm-stk,sto-10072-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Stockholm (.stk, .sto) message" id="10072" customizedId="FA_MESSAGE-Stockholm" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">Stockholm (.stk, .sto)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="Stockholm (.stk, .sto) progress bar 92" id="10073" customizedId="Stockholm-stk,sto-10073-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Stockholm (.stk, .sto) progress bar 92" id="10073" customizedId="FA_PROGRESSBAR-Stockholm" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="percentValue" type="int" value="92" />
                     </serializedBean>
                   </action>
 
-                  <action name="Stockholm (.stk, .sto) file association" id="10074" customizedId="Stockholm-stk,sto-10074-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .stk,sto file association">
+                  <action name="Stockholm (.stk, .sto) file association" id="10074" customizedId="FA_FILEASSOCIATION-Stockholm-true" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .stk,sto file association">
                     <serializedBean>
                       <property name="description" type="string">Stockholm File</property>
                       <property name="extension" type="string">stk,sto</property>
index 8663ed9..078076f 100644 (file)
@@ -1,5 +1,5 @@
 <!-- $$NAME$$ ($$DISPLAYEXTENSION$$) BEGIN -->
-                  <action name="$$NAME$$ ($$DISPLAYEXTENSION$$) message" id="$$ID$$" customizedId="$$NAME$$-$$EXTENSION$$-$$ID$$-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="$$NAME$$ ($$DISPLAYEXTENSION$$) message" id="$$ID$$" customizedId="FA_MESSAGE-$$NAME$$" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">$$NAME$$ ($$DISPLAYEXTENSION$$)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
@@ -8,13 +8,13 @@
                     </serializedBean>
                   </action>
 
-                  <action name="$$NAME$$ ($$DISPLAYEXTENSION$$) progress bar $$PROGRESSPERCENT$$" id="$$ID1$$" customizedId="$$NAME$$-$$EXTENSION$$-$$ID1$$-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="$$NAME$$ ($$DISPLAYEXTENSION$$) progress bar $$PROGRESSPERCENT$$" id="$$ID1$$" customizedId="FA_PROGRESSBAR-$$NAME$$" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="percentValue" type="int" value="$$PROGRESSPERCENT$$" />
                     </serializedBean>
                   </action>
 
-                  <action name="$$NAME$$ ($$DISPLAYEXTENSION$$) file association" id="$$ID2$$" customizedId="$$NAME$$-$$EXTENSION$$-$$ID2$$-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .$$EXTENSION$$ file association">
+                  <action name="$$NAME$$ ($$DISPLAYEXTENSION$$) file association" id="$$ID2$$" customizedId="FA_FILEASSOCIATION-$$NAME$$-$$PRIMARY$$" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .$$EXTENSION$$ file association">
                     <serializedBean>
                       <property name="description" type="string">$$NAME$$ File</property>
                       <property name="extension" type="string">$$EXTENSION$$</property>
index 6172279..9531fc6 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <install4j version="10.0.8" transformSequenceNumber="10">
-  <directoryPresets config="bin/Jalview" />
-  <application name="${compiler:JALVIEW_APPLICATION_NAME}" applicationId="${compiler:WINDOWS_APPLICATION_ID}" mediaDir="${compiler:BUILD_DIR}" lzmaCompression="true" shortName="${compiler:INTERNAL_ID}" publisher="University of Dundee" publisherWeb="https://www.jalview.org/" version="${compiler:JALVIEW_VERSION}" allPathsRelative="true" macVolumeId="5aac4968c304f65" javaMinVersion="${compiler:JAVA_MIN_VERSION}" javaMaxVersion="${compiler:JAVA_MAX_VERSION}" allowBetaVM="true" jdkMode="jdk" jdkName="JDK 11.0">
+  <directoryPresets config="." />
+  <application name="${compiler:JALVIEW_APPLICATION_NAME}" applicationId="${compiler:WINDOWS_APPLICATION_ID}" mediaDir="${compiler:JALVIEW_DIR}/${compiler:BUILD_DIR}" lzmaCompression="true" shortName="${compiler:INTERNAL_ID}" publisher="University of Dundee" publisherWeb="https://www.jalview.org/" version="${compiler:JALVIEW_VERSION}" allPathsRelative="true" macVolumeId="5aac4968c304f65" javaMinVersion="${compiler:JAVA_MIN_VERSION}" javaMaxVersion="${compiler:JAVA_MAX_VERSION}" allowBetaVM="true" jdkMode="jdk" jdkName="JDK 11.0">
     <searchSequence>
       <directory location="${compiler:JRE_DIR}" />
       <registry />
@@ -45,6 +45,9 @@
       <variable name="MACOSARCHIVE_VOLUMEICON" />
       <variable name="WRAPPER_LINK" value="jalview" />
       <variable name="BASH_WRAPPER_SCRIPT" value="jalview.sh" />
+      <variable name="POWERSHELL_WRAPPER_SCRIPT" value="jalview.ps1" />
+      <variable name="BASH_UPDATE_SCRIPT" value="update.sh" />
+      <variable name="POWERSHELL_UPDATE_SCRIPT" value="update.ps1" />
       <variable name="WRAPPER_SCRIPT_BIN_DIR" value="bin" />
       <variable name="MACOSARCHIVE_X64_NAME" value="Install Jalview (Intel)" />
       <variable name="MACOSARCHIVE_AARCH64_NAME" value="Install Jalview (Apple Silicon)" />
       <variable name="WINDOWS_ICONS_FILE" value="jalview_logo.ico" />
       <variable name="PNG_ICON_FILE" value="jalview_logo.png" />
       <variable name="BACKGROUND" value="utils/channels/release/images/jalview_logo_background_fade-640x480.png" />
-      <variable name="BATCH_WRAPPER_SCRIPT" value="jalview.bat" />
-      <variable name="POWERSHELL_WRAPPER_SCRIPT" value="jalview.ps1" />
-      <variable name="WIZARD_WIDTH" value="640" description="Default/initial width of installer wizard window.  Linux media types adapt this.&#xA;NOT USED" />
-      <variable name="WIZARD_HEIGHT" value="480" description="Default/initial width of installer wizard window.  Linux media types adapt this.&#xA;NOT USED" />
+      <variable name="WIZARD_WIDTH" value="960" description="Default/initial width of installer wizard window.  Linux media types adapt this.&#xA;NOT USED" />
+      <variable name="WIZARD_HEIGHT" value="720" description="Default/initial width of installer wizard window.  Linux media types adapt this.&#xA;NOT USED" />
       <variable name="MACOSARCHIVE_X64_DMG_FILENAME" value="${compiler:APPLICATION_FOLDER}-${compiler:JALVIEW_VERSION}-${compiler:sys.platform}-x64-java_${compiler:JAVA_INTEGER_VERSION}" />
       <variable name="MACOSARCHIVE_AARCH64_DMG_FILENAME" value="${compiler:APPLICATION_FOLDER}-${compiler:JALVIEW_VERSION}-${compiler:sys.platform}-aarch64-java_${compiler:JAVA_INTEGER_VERSION}" />
-      <variable name="INSTALLER_ICON" value="jalview_installer.png" />
-      <variable name="INSTALLER_MAC_ICON" value="jalview_installer.icns" />
-      <variable name="INSTALLER_WINDOWS_ICON" value="jalview_installer.ico" />
+      <variable name="INSTALLER_ICON" value="jalview_installer.png" description="An icon with installation arrow.&#xA;NOT USED" />
+      <variable name="INSTALLER_MAC_ICON" value="jalview_installer.icns" description="An icon with installation arrow.&#xA;NOT USED" />
+      <variable name="INSTALLER_WINDOWS_ICON" value="jalview_installer.ico" description="An icon with installation arrow.&#xA;NOT USED" />
+      <variable name="TITLE_ICON" value="jalview_logo-64.png" description="Icon sized for the installer wizard title bar" />
       <variable name="LOG_FILE" value=".jalview.log" />
     </variables>
     <codeSigning macEnabled="true" macPkcs12File="${compiler:OSX_KEYSTORE}" macNotarize="true" appleId="${compiler:OSX_APPLEID}">
       <dirEntry mountPoint="2806" file="${compiler:MACOS_AARCH64_JAVA_VM_DIR}" overwriteMode="1" fileMode="755" uninstallMode="2" overrideFileMode="true" overrideOverwriteMode="true" overrideUninstallMode="true" entryMode="subdir" subDirectory="${compiler:JRE_DIR}" />
     </entries>
     <components>
-      <component name="jalview_getdown" id="1031">
+      <component name="jalview_getdown" id="1031" changeable="false" hidden="true">
         <include>
           <entry filesetId="734" />
         </include>
   </files>
   <launchers>
     <launcher name="Jalview User Launcher" id="2823" customizedId="JALVIEW" menuName="${compiler:JALVIEW_APPLICATION_NAME}" icnsFile="${compiler:JALVIEW_DIR}/${compiler:ICONS_DIR}/${compiler:MAC_ICONS_FILE}" customMacBundleIdentifier="true" macBundleIdentifier="${compiler:BUNDLE_ID}" fileset="734" addMacApplicationCategory="true" macApplicationCategory="public.app-category.education" useCustomMacosExecutableName="true" customMacosExecutableName="${compiler:JALVIEW_APPLICATION_NAME}">
-      <executable name="${compiler:EXECUTABLE_NAME}" iconSet="true" iconFile="${compiler:JALVIEW_DIR}/${compiler:ICONS_DIR}/${compiler:WINDOWS_ICONS_FILE}" stderrFile="~/.${compiler:LOG_FILE}" redirectStdout="true" stdoutFile="~/.${compiler:LOG_FILE}" executableMode="gui" changeWorkingDirectory="false" singleInstance="true" executionLevel="highestAvailable" checkConsoleParameter="true">
+      <executable name="${compiler:EXECUTABLE_NAME}" iconSet="true" iconFile="${compiler:JALVIEW_DIR}/${compiler:ICONS_DIR}/${compiler:WINDOWS_ICONS_FILE}" stderrFile="~/.${compiler:LOG_FILE}" redirectStdout="true" stdoutFile="~/.${compiler:LOG_FILE}" executableMode="gui" changeWorkingDirectory="false" singleInstance="true" checkConsoleParameter="true">
         <versionInfo include="true" fileDescription="${compiler:sys.fullName}" legalCopyright="${compiler:COPYRIGHT_MESSAGE}" internalName="${compiler:INTERNAL_ID}" productName="${compiler:sys.fullName}" />
       </executable>
       <splashScreen width="640" height="480" bitmapFile="${compiler:JALVIEW_DIR}/${compiler:BACKGROUND}" textOverlay="true">
 # To include another file, uncomment the following line:
 # -include-options [path to other .vmoption file]
 
-# Uncomment these two lines to disable user-space automatic updates.
-#-Dnouserdefaultappdir=true
-#-Dsilent=noupdate</content>
+# Jalview specific options below</content>
       </vmOptionsFile>
       <infoPlist>${compiler:file("${compiler:INFO_PLIST_FILE_ASSOCIATIONS_FILE}")}</infoPlist>
       <iconImageFiles>
     </launcher>
   </launchers>
   <installerGui autoUpdateDescriptorUrl="https://www.jalview.org/install4j/updates.xml">
+    <staticMembers>/*
+
+import java.beans.BeanInfo;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.beans.IntrospectionException;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Array;
+
+  public static String printDataType(Object o) {
+    return printDataType("", o);
+  }
+  
+  public static String printDataType(String prefix, Object o) {
+      return printDataType(prefix, o, 1);
+  }
+  public static String printDataType(String prefix, Object o, int iter) {
+      StringBuilder stringRep = new StringBuilder();
+    
+      try {
+        if (o != null) {
+          Class objectType = o.getClass();
+          if (objectType.isPrimitive() ||
+              objectType.equals(String.class) ||
+              objectType.equals(Date.class)) {
+            stringRep.append(prefix + " (" + objectType.getSimpleName() + 
+              ") = " + o.toString() + "\n");
+          } else if (objectType.isArray()) {
+            if (Array.getLength(o) == 0)  {
+              stringRep.append(prefix + " = empty array\n");
+            }
+            if (objectType.getComponentType().isPrimitive() ||
+              objectType.getComponentType().equals(String.class) ||
+                objectType.getComponentType().equals(Date.class))  {
+              for (int i = 0; i &lt; Array.getLength(o); i++) {
+                stringRep.append(prefix + "[" + i + "] = " + 
+                  Array.get(o, i).toString() + "\n");
+              }
+            } else {
+              for (int i = 0; i &lt; Array.getLength(o); i++) {
+                stringRep.append(printDataType(prefix + "[" + i + "]", 
+                  Array.get(o, i), iter + 1));
+              }
+            }
+          } else if (o instanceof Iterable) {
+            int i = 0;
+            Iterator it = ((Iterable) o).iterator();
+            if (!it.hasNext()) {
+              stringRep.append(prefix + " = empty set\n");
+            }
+            while (it.hasNext()) {
+              stringRep.append(printDataType(prefix + "[" + i++ + "]", 
+                it.next(), iter + 1));
+            }
+          } else if (o instanceof Map)  {
+            Set&lt;Object&gt; keys = ((Map) o).keySet();
+            if (keys.isEmpty()) {
+              stringRep.append(prefix + " = empty map\n");
+            }
+            for (Object key : keys)  {
+              stringRep.append(printDataType(prefix + "[" + key.toString() + "]", 
+                ((Map) o).get(key), iter + 1));
+            }
+          } else if (o instanceof Class) {
+            // Do nothing otherwise the stack blows up
+          } else {
+            Method[] methods = objectType.getMethods();
+    
+            for (Method method : methods)  {
+              if (method.getName().startsWith("get") &amp;&amp;
+                method.getParameterCount() == 0)  {
+                String propertyName = method.getName().substring(3,4).toLowerCase() + 
+                  method.getName().substring(4);
+                Object value = method.invoke(o, new Object[] {} );
+                // value.getClass().isPrimitive returns false, even if 
+                // method.getReturnType().isPrimitive returns true
+                if (method.getReturnType().isPrimitive())  {
+                  stringRep.append(prefix + "." + propertyName + " (" + 
+                    method.getReturnType().getSimpleName() + ") = " + 
+                    value.toString() + "\n");
+                } else {
+                  if (iter &lt; 10) {
+                    stringRep.append(printDataType(prefix + "." + propertyName, value, iter + 1));
+                  } else {
+                    stringRep.append("...");
+                  }
+                }
+              } else if (method.getName().startsWith("is") &amp;&amp;
+                    method.getParameterCount() == 0 &amp;&amp;
+                    method.getReturnType().equals(boolean.class))  {
+                String propertyName = method.getName().substring(2,3).toLowerCase() + 
+                  method.getName().substring(3);
+                stringRep.append(prefix + "." + propertyName + " (boolean) = " +
+                    (method.invoke(o, new Object[] {}).equals(true) ? "true" : "false") + "\n");
+              }
+            }
+          }
+        } else {
+          return prefix + " = null\n";
+        }
+      } catch (Exception e)  {
+        //e.printStackTrace();
+        //return e.toString();
+        stringRep.append("Exception("+e.getMessage()+")");
+      }
+    
+      return stringRep.toString();
+    }
+
+*/
+
+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.getConfigurationObject()).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}">
+      <application id="installer" beanClass="com.install4j.runtime.beans.applications.InstallerApplication" actionElevationType="elevated" styleId="35" customIcnsFile="${compiler:JALVIEW_DIR}/${compiler:ICONS_DIR}/${compiler:MAC_ICONS_FILE}" customIcoFile="${compiler:JALVIEW_DIR}/${compiler:ICONS_DIR}/${compiler:WINDOWS_ICONS_FILE}">
         <serializedBean>
-          <property name="frameHeight" type="int" value="684" />
-          <property name="frameWidth" type="int" value="912" />
+          <property name="frameHeight" type="int" value="720" />
+          <property name="frameWidth" type="int" value="960" />
           <property name="useCustomIcon" type="boolean" value="true" />
         </serializedBean>
         <styleOverrides>
             </group>
           </styleOverride>
           <styleOverride name="Jalview" enabled="true">
-            <formComponent name="Watermark" id="352" beanClass="com.install4j.runtime.beans.formcomponents.SeparatorComponent" insetTop="0" insetLeft="5" insetBottom="0" useExternalParametrization="true" externalParametrizationName="Jalview" externalParametrizationMode="include">
+            <formComponent name="Watermark" id="352" beanClass="com.install4j.runtime.beans.formcomponents.SeparatorComponent" insetTop="0" insetLeft="5" insetBottom="0" useExternalParametrization="true" externalParametrizationName="Custom watermark" externalParametrizationMode="include">
               <serializedBean>
                 <property name="enabledTitleText" type="boolean" value="false" />
               </serializedBean>
                 <property name="imageEdgeBorderWidth" type="int" value="2" />
                 <property name="imageFile">
                   <object class="com.install4j.api.beans.ExternalFile">
-                    <string>${compiler:JALVIEW_DIR}/${compiler:INSTALLER_ICON}</string>
+                    <string>${compiler:JALVIEW_DIR}/${compiler:TITLE_ICON}</string>
                   </object>
                 </property>
                 <property name="imageInsets">
                 <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>
               </action>
               <action name="BOTHSPACE: Set unixBinDir (Linux or Unix)" id="2845" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction" rollbackBarrierExitCode="0">
                 <serializedBean>
+                  <property name="responseFileVariable" type="boolean" value="true" />
                   <property name="script">
                     <object class="com.install4j.api.beans.ScriptProperty">
                       <property name="value" type="string">ArrayList&lt;String&gt; tryPaths = new ArrayList&lt;&gt; ();
@@ -375,27 +719,216 @@ return null;
                   </property>
                   <property name="variableName" type="string">unixBinDir</property>
                 </serializedBean>
-                <condition>context.getBooleanVariable("makeSymbolicLink")</condition>
+                <condition>if (!context.getBooleanVariable("makeSymbolicLink")) {
+    return false;
+}
+String unixBinDir = (String) context.getVariable("unixBinDir");
+if (unixBinDir != null &amp;&amp; unixBinDir.length() &gt; 0) {
+    if (unixBinDir.startsWith("~/")) {
+        unixBinDir = (String)context.getVariable("sys.userHome") + unixBinDir.substring(1);
+        context.setVariable("unixBinDir", unixBinDir);
+    }
+    return false;
+}
+return true;</condition>
               </action>
-              <action name="USERSPACE: Set macWrapperLinkLocation (macOS)" id="2745" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction" rollbackBarrierExitCode="0">
+              <action name="USERSPACE: Set MacOSDir (macOS)" id="2745" 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 javaHome = System.getProperty("java.home");
 String appName = ((String)context.getCompilerVariable("JALVIEW_APPLICATION_NAME")) + ".app";
 int i = javaHome.indexOf(appName);
-String wrapperLink = null;
+String MacOSDir = null;
 if (i &gt; -1) {
-    wrapperLink = javaHome.substring(0, i) + appName + File.separator + "Contents" + File.separator + "MacOS" + File.separator + ((String)context.getCompilerVariable("WRAPPER_LINK"));
+    MacOSDir = javaHome.substring(0, i) + appName + File.separator + "Contents" + File.separator + "MacOS";
 }
-return wrapperLink;
+return MacOSDir;
 </property>
                     </object>
                   </property>
-                  <property name="variableName" type="string">macWrapperLinkLocation</property>
+                  <property name="variableName" type="string">MacOSDir</property>
                 </serializedBean>
                 <condition>Util.isMacOS() &amp;&amp; !context.getBooleanVariable("isAdmin") // Admin on macOS will add path to /etc/paths.d in Create File action</condition>
               </action>
+              <action name="BOTHSPACE: Set advancedOptions" id="3040" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction" rollbackBarrierExitCode="0">
+                <serializedBean>
+                  <property name="onlyIfUndefined" type="boolean" value="true" />
+                  <property name="responseFileVariable" type="boolean" value="true" />
+                  <property name="script">
+                    <object class="com.install4j.api.beans.ScriptProperty">
+                      <property name="value" type="string">Boolean.FALSE</property>
+                    </object>
+                  </property>
+                  <property name="variableName" type="string">advancedOptions</property>
+                </serializedBean>
+              </action>
+              <action name="BOTHSPACE: Set allowUserDefaultAppdirUpdates" id="2976" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction" rollbackBarrierExitCode="0">
+                <serializedBean>
+                  <property name="onlyIfUndefined" type="boolean" value="true" />
+                  <property name="responseFileVariable" type="boolean" value="true" />
+                  <property name="script">
+                    <object class="com.install4j.api.beans.ScriptProperty">
+                      <property name="value" type="string">Boolean.TRUE</property>
+                    </object>
+                  </property>
+                  <property name="variableName" type="string">allowUserDefaultAppdirUpdates</property>
+                </serializedBean>
+              </action>
+              <action name="BOTHSPACE: Set allowSetUserAppdirPath" id="3039" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction" rollbackBarrierExitCode="0">
+                <serializedBean>
+                  <property name="onlyIfUndefined" type="boolean" value="true" />
+                  <property name="responseFileVariable" type="boolean" value="true" />
+                  <property name="script">
+                    <object class="com.install4j.api.beans.ScriptProperty">
+                      <property name="value" type="string">Boolean.FALSE</property>
+                    </object>
+                  </property>
+                  <property name="variableName" type="string">allowSetUserAppdirPath</property>
+                </serializedBean>
+              </action>
+              <action name="BOTHSPACE: Set userAppdirPath" id="3038" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction" rollbackBarrierExitCode="0">
+                <serializedBean>
+                  <property name="onlyIfUndefined" type="boolean" value="true" />
+                  <property name="responseFileVariable" type="boolean" value="true" />
+                  <property name="script">
+                    <object class="com.install4j.api.beans.ScriptProperty">
+                      <property name="value" type="string">""</property>
+                    </object>
+                  </property>
+                  <property name="variableName" type="string">userAppdirPath</property>
+                </serializedBean>
+              </action>
+              <action name="BOTHSPACE: Set allowInstallerAppdirUpdates" id="2977" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction" rollbackBarrierExitCode="0">
+                <serializedBean>
+                  <property name="onlyIfUndefined" type="boolean" value="true" />
+                  <property name="responseFileVariable" type="boolean" value="true" />
+                  <property name="script">
+                    <object class="com.install4j.api.beans.ScriptProperty">
+                      <property name="value" type="string">Boolean.FALSE</property>
+                    </object>
+                  </property>
+                  <property name="variableName" type="string">allowInstallerAppdirUpdates</property>
+                </serializedBean>
+              </action>
+              <action name="BOTHSPACE: Set userDefaultAppdirBase for tooltip explanation" id="2981" 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">getOsAppDataPath(context)</property>
+                    </object>
+                  </property>
+                  <property name="variableName" type="string">userDefaultAppdirBase</property>
+                </serializedBean>
+              </action>
+              <action name="BOTHSPACE: Set osName variable for tooltip explanation" id="2982" 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">if (Util.isWindows()) {
+    return "Windows";
+} else if (Util.isMacOS()) {
+    return "macOS";
+} else if (Util.isLinux()) {
+    return "Linux";
+}
+return context.getCompilerVariable("sys.platform");</property>
+                    </object>
+                  </property>
+                  <property name="variableName" type="string">osName</property>
+                </serializedBean>
+              </action>
+              <action name="CONSOLE: Set consoleDisableUserAppdir" id="3075" 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">Boolean.FALSE</property>
+                    </object>
+                  </property>
+                  <property name="variableName" type="string">consoleDisableUserAppdir</property>
+                </serializedBean>
+              </action>
+              <action name="CONSOLE: Set consoleDisableAllUpdates" id="3076" 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">Boolean.FALSE</property>
+                    </object>
+                  </property>
+                  <property name="variableName" type="string">consoleDisableAllUpdates</property>
+                </serializedBean>
+              </action>
+              <action name="CONSOLE: Set consoleAllowUserAppdirPath" id="3077" 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">Boolean.FALSE</property>
+                    </object>
+                  </property>
+                  <property name="variableName" type="string">consoleAllowUserAppdirPath</property>
+                </serializedBean>
+              </action>
+              <action name="Extra Command Line Options" id="3074" beanClass="com.install4j.runtime.beans.actions.control.RunScriptAction" rollbackBarrierExitCode="0">
+                <serializedBean>
+                  <property name="script">
+                    <object class="com.install4j.api.beans.ScriptProperty">
+                      <property name="value" type="string">String[] args = context.getExtraCommandLineArguments();
+
+for (int i = 0; i &lt; args.length; i++) {
+    String arg = args[i];
+    switch(arg) {
+        case "-a":
+            if (args.length - 1 &lt; i + 1) {
+                System.out.println("Option " + arg + " requires a value.  Ignoring.");
+            }
+            context.setInstallationDirectory(new File(args[i + 1]));
+            i++;
+            break;
+        case "-u":
+            if (args.length - 1 &lt; i + 1) {
+                System.out.println("Option " + arg + " requires a value.  Ignoring.");
+            }
+            context.setVariable("userAppdirPath", args[i + 1]);
+            context.setVariable("consoleAllowUserAppdirPath", true);
+            i++;        
+            break;
+        case "-U":
+            context.setVariable("consoleDisableUserAppdir", true);
+            break;
+        case "-S":
+            context.setVariable("consoleDisableAllUpdates", true);
+            break;
+        default:
+            System.out.println("Option " + arg + " not recognised.  Ignoring.");
+            break;
+    }
+}
+
+return true;</property>
+                    </object>
+                  </property>
+                </serializedBean>
+                <condition>context.isConsole() || context.isUnattended()</condition>
+              </action>
+              <action name="Set sys.fileAssociation.launchers correctly" id="3080" beanClass="com.install4j.runtime.beans.actions.control.RunScriptAction" rollbackBarrierExitCode="0">
+                <serializedBean>
+                  <property name="script">
+                    <object class="com.install4j.api.beans.ScriptProperty">
+                      <property name="value" type="string">String[] extensions = (String[]) context.getVariable("sys.fileAssociation.extensions");
+if (extensions == null) {
+    return false;
+}
+int num = extensions.length;
+String[] launchers = new String[num];
+for (int i = 0; i &lt; num; i++) {
+    launchers[i] = "JALVIEW";
+}
+context.setVariable("sys.fileAssociation.launchers", launchers);
+return true;</property>
+                    </object>
+                  </property>
+                </serializedBean>
+              </action>
             </actions>
           </screen>
         </startup>
@@ -468,14 +1001,24 @@ return wrapperLink;
             <actions>
               <action id="7" beanClass="com.install4j.runtime.beans.actions.misc.LoadResponseFileAction" rollbackBarrierExitCode="0" multiExec="true">
                 <serializedBean>
-                  <property name="excludedVariables" type="array" elementType="string" length="1">
+                  <property name="excludedVariables" type="array" elementType="string" length="5">
                     <element index="0">sys.installationDir</element>
+                    <element index="1">sys.adminRights$Boolean</element>
+                    <element index="2">sys.adminRightsUiRootUnix$Boolean</element>
+                    <element index="3">sys.component.1031$Boolean</element>
+                    <element index="4">sys.fileAssociation.launchers$StringArray</element>
                   </property>
                 </serializedBean>
                 <condition>context.getBooleanVariable("sys.confirmedUpdateInstallation")</condition>
               </action>
             </actions>
             <formComponents>
+              <formComponent name="Administrator mode label" id="3028" beanClass="com.install4j.runtime.beans.formcomponents.MultilineHtmlLabelComponent" insetBottom="16">
+                <serializedBean>
+                  <property name="labelHtml" type="string">&lt;strong&gt;Administrator mode&lt;/strong&gt;</property>
+                </serializedBean>
+                <visibilityScript>context.getBooleanVariable("isAdmin")</visibilityScript>
+              </formComponent>
               <formComponent id="3" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent">
                 <serializedBean>
                   <property name="labelText" type="string">${form:welcomeMessage}</property>
@@ -505,19 +1048,29 @@ return console.askOkCancel(message, true);
               </formComponent>
             </formComponents>
           </screen>
-          <screen id="8" beanClass="com.install4j.runtime.beans.screens.InstallationDirectoryScreen" rollbackBarrierExitCode="0">
+          <screen id="8" customizedId="LOCATION_SCREEN" beanClass="com.install4j.runtime.beans.screens.InstallationDirectoryScreen" rollbackBarrierExitCode="0">
             <condition>!context.getBooleanVariable("sys.confirmedUpdateInstallation")</condition>
             <actions>
               <action id="11" beanClass="com.install4j.runtime.beans.actions.misc.LoadResponseFileAction" rollbackBarrierExitCode="0" multiExec="true">
                 <serializedBean>
-                  <property name="excludedVariables" type="array" elementType="string" length="1">
+                  <property name="excludedVariables" type="array" elementType="string" length="5">
                     <element index="0">sys.installationDir</element>
+                    <element index="1">sys.adminRights$Boolean</element>
+                    <element index="2">sys.adminRightsUiRootUnix$Boolean</element>
+                    <element index="3">sys.component.1031$Boolean</element>
+                    <element index="4">sys.fileAssociation.launchers$StringArray</element>
                   </property>
                 </serializedBean>
                 <condition>context.getVariable("sys.responseFile") == null</condition>
               </action>
             </actions>
             <formComponents>
+              <formComponent name="Administrator mode label" id="3057" beanClass="com.install4j.runtime.beans.formcomponents.MultilineHtmlLabelComponent" insetBottom="16">
+                <serializedBean>
+                  <property name="labelHtml" type="string">&lt;strong&gt;Administrator mode&lt;/strong&gt;</property>
+                </serializedBean>
+                <visibilityScript>context.getBooleanVariable("isAdmin")</visibilityScript>
+              </formComponent>
               <formComponent id="9" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" insetBottom="25">
                 <serializedBean>
                   <property name="labelText" type="string">${i18n:SelectDirLabel(${compiler:sys.fullName})}</property>
@@ -545,27 +1098,61 @@ return console.askOkCancel(message, true);
           </screen>
           <screen id="1692" beanClass="com.install4j.runtime.beans.screens.FileAssociationsScreen" rollbackBarrierExitCode="0">
             <formComponents>
+              <formComponent name="Administrator mode label" id="3058" beanClass="com.install4j.runtime.beans.formcomponents.MultilineHtmlLabelComponent" insetBottom="16">
+                <serializedBean>
+                  <property name="labelHtml" type="string">&lt;strong&gt;Administrator mode&lt;/strong&gt;</property>
+                </serializedBean>
+                <visibilityScript>context.getBooleanVariable("isAdmin")</visibilityScript>
+              </formComponent>
               <formComponent id="1693" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent">
                 <serializedBean>
                   <property name="labelText" type="string">${i18n:SelectAssociationsLabel}</property>
                 </serializedBean>
               </formComponent>
-              <formComponent id="1694" beanClass="com.install4j.runtime.beans.formcomponents.FileAssociationsComponent" useExternalParametrization="true" externalParametrizationName="File Associations" externalParametrizationMode="include">
+              <formComponent id="3069" beanClass="com.install4j.runtime.beans.formcomponents.ButtonComponent" enabled="false" commentSet="true" comment="This button is desirable but not working.&#xA;The best that can be done is&#xA;&#xA;Screen s = formEnvironment.getScreen();&#xA;context.gotoScreen(s);&#xA;&#xA;but this adds extra &quot;File Association&quot; screens into the &quot;Back&quot; history (but not the &quot;Next&quot; stack).">
+                <serializedBean>
+                  <property name="actionScript">
+                    <object class="com.install4j.api.beans.ScriptProperty">
+                      <property name="value" type="string">Screen s = formEnvironment.getScreen();
+
+FormComponent fc = formEnvironment.getFormComponentById("FILE_ASSOCIATIONS_SELECTOR");
+com.install4j.api.actions.Action a0 = context.getActionById("FA_FILEASSOCIATION-CIF-false");
+Screen s2 = context.getScreenById("EXTENSIONS_REPLACED_BY_GRADLE_PARENT_GROUP");
+
+for (com.install4j.api.actions.Action a : context.getActions(s2)) {
+    String aid = context.getId(a);
+    if (aid.startsWith("FA_FILEASSOCIATION-")) {
+        com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction aa = (com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction) a;
+        boolean set = aid.endsWith("-true");
+        boolean got = aa.isSelected();
+        aa.setSelected(set);
+        System.err.println("Setting '"+aid+"' from "+got+" to "+set);
+    }
+}
+
+context.gotoScreen(s);
+//formEnvironment.reinitializeFormComponents();</property>
+                    </object>
+                  </property>
+                  <property name="buttonText" type="string">Reset</property>
+                </serializedBean>
+              </formComponent>
+              <formComponent id="1694" customizedId="FILE_ASSOCIATIONS_SELECTOR" beanClass="com.install4j.runtime.beans.formcomponents.FileAssociationsComponent" useExternalParametrization="true" externalParametrizationName="File Associations" externalParametrizationMode="include">
                 <serializedBean>
                   <property name="fillVertical" type="boolean" value="true" />
                   <property name="selectionButtonPosition" type="enum" class="com.install4j.runtime.beans.formcomponents.VerticalDockingPosition" value="TOP" />
                   <property name="showSelectionButtons" type="boolean" value="true" />
                 </serializedBean>
                 <externalParametrizationPropertyNames>
-                  <propertyName>showSelectionButtons</propertyName>
                   <propertyName>selectionButtonPosition</propertyName>
+                  <propertyName>showSelectionButtons</propertyName>
                 </externalParametrizationPropertyNames>
               </formComponent>
             </formComponents>
           </screen>
           <screen name="USERSPACE: Additional checkboxes" id="2893" beanClass="com.install4j.runtime.beans.screens.FormScreen" rollbackBarrierExitCode="0">
             <serializedBean>
-              <property name="subTitle" type="string">User account</property>
+              <property name="subTitle" type="string">Additional tasks for user installation (${installer:sys.userName})</property>
               <property name="title" type="string">${i18n:WizardSelectTasks}</property>
             </serializedBean>
             <condition>!context.getBooleanVariable("isAdmin")</condition>
@@ -610,19 +1197,144 @@ return console.askOkCancel(message, true);
      || Util.isUnixInstaller()
      || (
           ( Util.isMacOS() &amp;&amp; !Util.hasFullAdminRights() ) // Admin on macOS will add path to /etc/paths.d
-          &amp;&amp; context.getVariable("macWrapperLinkLocation") != null
+          &amp;&amp; context.getVariable("MacOSDir") != null
         )
     )</visibilityScript>
               </formComponent>
+              <formComponent id="2983" beanClass="com.install4j.runtime.beans.formcomponents.SpacerComponent" />
+              <formComponent name="Enable ADVANCED OPTIONS" id="3006" customizedId="US_ADVANCED_OPTIONS" beanClass="com.install4j.runtime.beans.formcomponents.CheckboxComponent">
+                <serializedBean>
+                  <property name="checkboxText" type="string">Enable advanced options for user installation</property>
+                  <property name="coupledComponentIds">
+                    <add type="string">2980</add>
+                    <add type="string">3014</add>
+                    <add type="string">2975</add>
+                    <add type="string">2989</add>
+                  </property>
+                  <property name="selectionScript">
+                    <object class="com.install4j.api.beans.ScriptProperty">
+                      <property name="value" type="string">validateUserSpaceAdvancedOptionsForm(formEnvironment)</property>
+                    </object>
+                  </property>
+                  <property name="variableName" type="string">advancedOptions</property>
+                </serializedBean>
+              </formComponent>
+              <formComponent name="Advanced options not used label" id="3036" customizedId="US_NOT_USED" beanClass="com.install4j.runtime.beans.formcomponents.LabelComponent" insetLeft="20">
+                <serializedBean>
+                  <property name="labelColor">
+                    <object class="java.awt.Color">
+                      <int>128</int>
+                      <int>128</int>
+                      <int>128</int>
+                      <int>255</int>
+                    </object>
+                  </property>
+                  <property name="labelFontSizePercent" type="int" value="80" />
+                  <property name="labelFontType" type="enum" class="com.install4j.runtime.beans.formcomponents.FontType" value="DERIVED" />
+                  <property name="labelText" type="string">Advanced options will not be used</property>
+                </serializedBean>
+                <visibilityScript>!context.getBooleanVariable("advancedOptions")
+</visibilityScript>
+              </formComponent>
+              <group name="Advanced options (outer) group" id="3035" customizedId="US_ADVANCED_OPTIONS_GROUP" beanClass="com.install4j.runtime.beans.groups.VerticalFormComponentGroup">
+                <serializedBean>
+                  <property name="visibilityScript">
+                    <object class="com.install4j.api.beans.ScriptProperty">
+                      <property name="value" type="string">FormComponent fc_advancedOptions = formEnvironment.getFormComponentById("US_ADVANCED_OPTIONS");
+
+JCheckBox jcb_advancedOptions = (JCheckBox) fc_advancedOptions.getConfigurationObject();
+boolean advancedOptions = jcb_advancedOptions.isSelected();
+
+return advancedOptions;</property>
+                    </object>
+                  </property>
+                </serializedBean>
+                <beans>
+                  <group name="Advanced options (boxed) group" id="3034" beanClass="com.install4j.runtime.beans.groups.VerticalFormComponentGroup">
+                    <serializedBean>
+                      <property name="borderSides">
+                        <object class="com.install4j.runtime.beans.formcomponents.BorderSides">
+                          <property name="bottom" type="boolean" value="true" />
+                          <property name="left" type="boolean" value="true" />
+                          <property name="right" type="boolean" value="true" />
+                          <property name="top" type="boolean" value="true" />
+                        </object>
+                      </property>
+                      <property name="insets">
+                        <object class="java.awt.Insets">
+                          <int>4</int>
+                          <int>4</int>
+                          <int>4</int>
+                          <int>4</int>
+                        </object>
+                      </property>
+                    </serializedBean>
+                    <beans>
+                      <formComponent name="Strongly recommended text" id="2980" customizedId="US_LABEL" beanClass="com.install4j.runtime.beans.formcomponents.MultilineHtmlLabelComponent" insetLeft="16">
+                        <serializedBean>
+                          <property name="labelHtml" type="string">&lt;html&gt;The following option is &lt;strong&gt;strongly recommended&lt;/strong&gt;
+to be left as default unless there is a particular
+reason to change it.&lt;/html&gt;</property>
+                        </serializedBean>
+                      </formComponent>
+                      <formComponent id="3014" customizedId="US_VSPACE" beanClass="com.install4j.runtime.beans.formcomponents.SpacerComponent" insetLeft="16" />
+                      <formComponent name="BOTHSPACE: Allow automatic updates in user's home directory" id="2975" customizedId="US_ALLOW_USER_APPDIR_UPDATES" beanClass="com.install4j.runtime.beans.formcomponents.CheckboxComponent" insetLeft="16">
+                        <serializedBean>
+                          <property name="checkboxText" type="string">Allow user-space updates for ${compiler:JALVIEW_APPLICATION_NAME} components</property>
+                          <property name="helpText" type="string">&lt;html&gt;This option allows updates to ${compiler:JALVIEW_APPLICATION_NAME}
+&lt;br&gt;
+components to be automatically downloaded
+&lt;br&gt;
+under the user's home space, separately from
+&lt;br&gt;
+the installation location.
+&lt;br&gt;
+&lt;strong&gt;This option is strongly recommended.&lt;/strong&gt;
+&lt;br&gt;
+&lt;br&gt;
+On ${installer:osName}, user updates will be installed under
+&lt;br&gt;
+&lt;pre&gt;${installer:userDefaultAppdirBase}&lt;/pre&gt;
+&lt;/html&gt;</property>
+                          <property name="initiallySelected" type="boolean" value="true" />
+                          <property name="selectionScript">
+                            <object class="com.install4j.api.beans.ScriptProperty">
+                              <property name="value" type="string">validateUserSpaceAdvancedOptionsForm(formEnvironment)</property>
+                            </object>
+                          </property>
+                          <property name="variableName" type="string">allowUserDefaultAppdirUpdates</property>
+                        </serializedBean>
+                      </formComponent>
+                      <formComponent name="BOTHSPACE: No updates warning" id="2989" customizedId="US_NO_UPDATES_WARNING" beanClass="com.install4j.runtime.beans.formcomponents.LabelComponent" insetLeft="16">
+                        <serializedBean>
+                          <property name="labelIconFile">
+                            <object class="com.install4j.api.beans.ExternalFile">
+                              <string>${compiler:JALVIEW_DIR}/${compiler:INSTALL4J_UTILS_DIR}/warning.png</string>
+                            </object>
+                          </property>
+                          <property name="labelText" type="string">No automatic updates will occur when Jalview is launched</property>
+                        </serializedBean>
+                        <visibilityScript>!( context.getBooleanVariable("allowUserDefaultAppdirUpdates") || context.getBooleanVariable("allowInstallerAppdirUpdates") )</visibilityScript>
+                      </formComponent>
+                    </beans>
+                  </group>
+                </beans>
+              </group>
             </formComponents>
           </screen>
           <screen name="SYSTEMSPACE: Additional checkboxes" id="2903" beanClass="com.install4j.runtime.beans.screens.FormScreen" rollbackBarrierExitCode="0">
             <serializedBean>
-              <property name="subTitle" type="string">System</property>
+              <property name="subTitle" type="string">Additional tasks for administrator installation</property>
               <property name="title" type="string">${i18n:WizardSelectTasks}</property>
             </serializedBean>
             <condition>context.getBooleanVariable("isAdmin")</condition>
             <formComponents>
+              <formComponent name="Administrator mode label" id="3059" beanClass="com.install4j.runtime.beans.formcomponents.MultilineHtmlLabelComponent" insetBottom="16">
+                <serializedBean>
+                  <property name="labelHtml" type="string">&lt;strong&gt;Administrator mode&lt;/strong&gt;</property>
+                </serializedBean>
+                <visibilityScript>context.getBooleanVariable("isAdmin")</visibilityScript>
+              </formComponent>
               <formComponent id="2904" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" insetBottom="10">
                 <serializedBean>
                   <property name="labelText" type="string">${i18n:SelectTasksLabel2(${compiler:JALVIEW_APPLICATION_NAME})}</property>
@@ -649,14 +1361,313 @@ return console.askOkCancel(message, true);
      || Util.isUnixInstaller()
      || (
           ( Util.isMacOS() &amp;&amp; !Util.hasFullAdminRights() ) // Admin on macOS will add path to /etc/paths.d
-          &amp;&amp; context.getVariable("macWrapperLinkLocation") != null
+          &amp;&amp; context.getVariable("MacOSDir") != null
         )
     )
 </visibilityScript>
               </formComponent>
+              <formComponent id="2984" beanClass="com.install4j.runtime.beans.formcomponents.SpacerComponent" />
+              <formComponent name="Enable ADVANCED OPTIONS" id="3007" customizedId="SS_ADVANCED_OPTIONS" beanClass="com.install4j.runtime.beans.formcomponents.CheckboxComponent">
+                <serializedBean>
+                  <property name="checkboxText" type="string">Enable advanced options for system installation</property>
+                  <property name="coupledComponentIds">
+                    <add type="string">3030</add>
+                    <add type="string">2985</add>
+                    <add type="string">3013</add>
+                    <add type="string">2986</add>
+                    <add type="string">2974</add>
+                    <add type="string">2988</add>
+                  </property>
+                  <property name="selectionScript">
+                    <object class="com.install4j.api.beans.ScriptProperty">
+                      <property name="value" type="string">validateSystemSpaceAdvancedOptionsForm(context, formEnvironment)</property>
+                    </object>
+                  </property>
+                  <property name="variableName" type="string">advancedOptions</property>
+                </serializedBean>
+              </formComponent>
+              <formComponent name="Advanced options not used label" id="3032" customizedId="SS_NOT_USED" beanClass="com.install4j.runtime.beans.formcomponents.LabelComponent" insetLeft="20">
+                <serializedBean>
+                  <property name="labelColor">
+                    <object class="java.awt.Color">
+                      <int>128</int>
+                      <int>128</int>
+                      <int>128</int>
+                      <int>255</int>
+                    </object>
+                  </property>
+                  <property name="labelFontSizePercent" type="int" value="80" />
+                  <property name="labelFontType" type="enum" class="com.install4j.runtime.beans.formcomponents.FontType" value="DERIVED" />
+                  <property name="labelText" type="string">Advanced options will not be used</property>
+                </serializedBean>
+                <visibilityScript>!context.getBooleanVariable("advancedOptions")
+</visibilityScript>
+              </formComponent>
+              <group name="Advanced options (outer) group" id="3033" customizedId="SS_ADVANCED_OPTIONS_GROUP" beanClass="com.install4j.runtime.beans.groups.VerticalFormComponentGroup">
+                <serializedBean>
+                  <property name="visibilityScript">
+                    <object class="com.install4j.api.beans.ScriptProperty">
+                      <property name="value" type="string">context.getBooleanVariable("advancedOptions")
+</property>
+                    </object>
+                  </property>
+                </serializedBean>
+                <beans>
+                  <group name="Advanced options (boxed) group" id="3030" beanClass="com.install4j.runtime.beans.groups.VerticalFormComponentGroup">
+                    <serializedBean>
+                      <property name="borderSides">
+                        <object class="com.install4j.runtime.beans.formcomponents.BorderSides">
+                          <property name="bottom" type="boolean" value="true" />
+                          <property name="left" type="boolean" value="true" />
+                          <property name="right" type="boolean" value="true" />
+                          <property name="top" type="boolean" value="true" />
+                        </object>
+                      </property>
+                      <property name="insets">
+                        <object class="java.awt.Insets">
+                          <int>4</int>
+                          <int>4</int>
+                          <int>4</int>
+                          <int>4</int>
+                        </object>
+                      </property>
+                      <property name="visibilityScript">
+                        <object class="com.install4j.api.beans.ScriptProperty">
+                          <property name="value" type="string" />
+                        </object>
+                      </property>
+                    </serializedBean>
+                    <beans>
+                      <formComponent name="Strongly recommended text" id="2985" customizedId="SS_LABEL" beanClass="com.install4j.runtime.beans.formcomponents.MultilineHtmlLabelComponent" insetLeft="16">
+                        <serializedBean>
+                          <property name="labelHtml" type="string">&lt;html&gt;The following options are &lt;strong&gt;strongly recommended&lt;/strong&gt; to be left as default unless there is a particular reason to change them.&lt;/html&gt;</property>
+                        </serializedBean>
+                      </formComponent>
+                      <formComponent id="3013" customizedId="SS_VSPACE" beanClass="com.install4j.runtime.beans.formcomponents.SpacerComponent" insetLeft="16" />
+                      <formComponent name="BOTHSPACE: Allow automatic updates in user's home directory" id="2986" customizedId="SS_ALLOW_USER_APPDIR_UPDATES" beanClass="com.install4j.runtime.beans.formcomponents.CheckboxComponent" insetLeft="16">
+                        <serializedBean>
+                          <property name="checkboxText" type="string">Allow user-space updates for ${compiler:JALVIEW_APPLICATION_NAME} components</property>
+                          <property name="coupledComponentIds">
+                            <add type="string">3021</add>
+                          </property>
+                          <property name="helpText" type="string">&lt;html&gt;This option allows updates to ${compiler:JALVIEW_APPLICATION_NAME}
+&lt;br&gt;
+components to be automatically downloaded
+&lt;br&gt;
+under the user's home space, separately from
+&lt;br&gt;
+the installation location.
+&lt;br&gt;
+&lt;strong&gt;This option is strongly recommended.&lt;/strong&gt;
+&lt;br&gt;
+&lt;br&gt;
+On ${installer:osName}, user updates will be installed under
+&lt;pre&gt;${installer:userDefaultAppdirBase}&lt;/pre&gt;
+unless customised below.
+&lt;/html&gt;</property>
+                          <property name="selectionScript">
+                            <object class="com.install4j.api.beans.ScriptProperty">
+                              <property name="value" type="string">validateSystemSpaceAdvancedOptionsForm(context, formEnvironment)</property>
+                            </object>
+                          </property>
+                          <property name="variableName" type="string">allowUserDefaultAppdirUpdates</property>
+                        </serializedBean>
+                      </formComponent>
+                      <group name="SYSTEMSPACE: Customise user appDir path" id="3021" customizedId="SS_SET_USER_APPDIR_PATH" beanClass="com.install4j.runtime.beans.groups.HorizontalFormComponentGroup">
+                        <serializedBean>
+                          <property name="insets">
+                            <object class="java.awt.Insets">
+                              <int>0</int>
+                              <int>32</int>
+                              <int>0</int>
+                              <int>0</int>
+                            </object>
+                          </property>
+                          <property name="visibilityScript">
+                            <object class="com.install4j.api.beans.ScriptProperty">
+                              <property name="value" type="string" />
+                            </object>
+                          </property>
+                        </serializedBean>
+                        <beans>
+                          <formComponent name="SYSTEMSPACE: Allow setting userAppdirPath" id="3022" customizedId="SS_ALLOW_USER_APPDIR_PATH" beanClass="com.install4j.runtime.beans.formcomponents.CheckboxComponent">
+                            <serializedBean>
+                              <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">validateSystemSpaceAdvancedOptionsForm(context, formEnvironment)</property>
+                                </object>
+                              </property>
+                              <property name="variableName" type="string">allowSetUserAppdirPath</property>
+                            </serializedBean>
+                            <initScript>component.setEnabled( context.getBooleanVariable("allowUserDefaultAppdirUpdates") )</initScript>
+                          </formComponent>
+                          <formComponent name="SYSTEMSPACE: Set userAppdirPath" id="3024" customizedId="SS_USER_APPDIR_PATH" beanClass="com.install4j.runtime.beans.formcomponents.TextfieldComponent">
+                            <serializedBean>
+                              <property name="helpText" type="string">&lt;html&gt;The base path where individual users' updates
+&lt;br&gt;
+will be stored.
+&lt;br&gt;
+&lt;strong&gt;Only change this option if you need to!&lt;/strong&gt;
+&lt;br&gt;
+The following substitutions will be made:
+&lt;table&gt;
+&lt;tr&gt;&lt;td&gt;%u&lt;/td&gt;&lt;td&gt;The user's username&lt;/td&gt;&lt;/tr&gt;
+&lt;tr&gt;&lt;td&gt;%h&lt;/td&gt;&lt;td&gt;The user's home directory path&lt;/td&gt;&lt;/tr&gt;
+&lt;tr&gt;&lt;td&gt;~${installer:sys.fileSeparator}&lt;/td&gt;&lt;td&gt;(at start) The user's home directory path${installer:sys.fileSeparator}&lt;/td&gt;&lt;/tr&gt;
+&lt;/table&gt;
+At least one of the above substitutions should be
+&lt;br&gt;
+used, e.g. &lt;tt&gt;/tmp/${compiler:UNIX_APPLICATION_FOLDER}/%u/app&lt;/tt&gt;
+&lt;br&gt;
+The default value on ${installer:osName} is
+&lt;br&gt;
+&lt;pre&gt;${installer:userDefaultAppdirBase}&lt;/pre&gt;
+&lt;/html&gt;</property>
+                              <property name="inputVerifier">
+                                <object class="com.install4j.api.beans.ScriptProperty">
+                                  <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">showInvalidUserAppdirPathWarning(context, formEnvironment)
+</property>
+                                </object>
+                              </property>
+                              <property name="keyValidator">
+                                <object class="com.install4j.api.beans.ScriptProperty">
+                                  <property name="value" type="string" />
+                                </object>
+                              </property>
+                              <property name="variableName" type="string">userAppdirPath</property>
+                            </serializedBean>
+                            <initScript>component.setEnabled( context.getBooleanVariable("allowUserDefaultAppdirUpdates") &amp;&amp; context.getBooleanVariable("allowSetUserAppdirPath") );
+</initScript>
+                          </formComponent>
+                        </beans>
+                      </group>
+                      <formComponent name="SYSTEMSPACE: Allow automatic updates in the installation directory" id="2974" customizedId="SS_ALLOW_INSTALLER_APPDIR_UPDATES" beanClass="com.install4j.runtime.beans.formcomponents.CheckboxComponent" insetLeft="16">
+                        <serializedBean>
+                          <property name="checkboxText" type="string">Allow installation updates for ${compiler:JALVIEW_APPLICATION_NAME} components</property>
+                          <property name="helpText" type="string">&lt;html&gt;This option allows updates to ${compiler:JALVIEW_APPLICATION_NAME}
+&lt;br&gt;
+components to be automatically downloaded
+&lt;br&gt;
+into the installation location.
+&lt;br&gt;
+If you are installing into a system location,
+&lt;br&gt;
+a non-administrator user may have problems
+&lt;br&gt;
+launching ${compiler:JALVIEW_APPLICATION_NAME}.
+&lt;br&gt;
+&lt;strong&gt;
+It is strongly recommended to use the
+&lt;br&gt;
+user-space updates option above.
+&lt;/strong&gt;
+&lt;br&gt;
+&lt;br&gt;
+Installation updates will be installed into
+&lt;br&gt;
+&lt;pre&gt;${installer:sys.contentDir}&lt;/pre&gt;
+&lt;/html&gt;</property>
+                          <property name="selectionScript">
+                            <object class="com.install4j.api.beans.ScriptProperty">
+                              <property name="value" type="string">validateSystemSpaceAdvancedOptionsForm(context, formEnvironment)</property>
+                            </object>
+                          </property>
+                          <property name="variableName" type="string">allowInstallerAppdirUpdates</property>
+                        </serializedBean>
+                        <initScript>component.setEnabled( !context.getBooleanVariable("allowUserDefaultAppdirUpdates") )</initScript>
+                      </formComponent>
+                      <formComponent name="Set defaults" id="3029" customizedId="SS_SET_DEFAULTS" beanClass="com.install4j.runtime.beans.formcomponents.ButtonComponent" insetTop="8" insetLeft="16">
+                        <serializedBean>
+                          <property name="actionScript">
+                            <object class="com.install4j.api.beans.ScriptProperty">
+                              <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");
+
+// set defaults
+((JCheckBox) fc_userUpdates.getConfigurationObject()).setSelected(true);
+((JCheckBox) fc_allowUserAppdirPath.getConfigurationObject()).setSelected(false);
+((JTextField) fc_userAppdirPath.getConfigurationObject()).setText("");
+((JCheckBox) fc_installerUpdates.getConfigurationObject()).setSelected(false);
+
+// revalidate
+validateSystemSpaceAdvancedOptionsForm(context, formEnvironment);
+</property>
+                            </object>
+                          </property>
+                          <property name="buttonText" type="string">Reset advanced options to defaults</property>
+                        </serializedBean>
+                        <initScript>FormComponent fc_advancedOptions = formEnvironment.getFormComponentById("SS_ADVANCED_OPTIONS");
+FormComponent fc_userUpdates = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_UPDATES");
+FormComponent fc_userAppdirPath = formEnvironment.getFormComponentById("SS_USER_APPDIR_PATH");
+FormComponent fc_installerUpdates = formEnvironment.getFormComponentById("SS_ALLOW_INSTALLER_APPDIR_UPDATES");
+FormComponent fc_allowUserAppdirPath = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_PATH");
+
+JCheckBox jcb_user = (JCheckBox) fc_userUpdates.getConfigurationObject();
+boolean userUpdates = jcb_user.isSelected();
+
+JCheckBox jcb_allowUserAppdirPath = (JCheckBox) fc_allowUserAppdirPath.getConfigurationObject();
+boolean allowUserAppdirPath = jcb_allowUserAppdirPath.isSelected();
+
+JTextField jtf_userAppdirPath = (JTextField) fc_userAppdirPath.getConfigurationObject();
+String userAppdirPath = jtf_userAppdirPath.getText();
+
+JCheckBox jcb_installer = (JCheckBox) fc_installerUpdates.getConfigurationObject();
+boolean installerUpdates = jcb_installer.isSelected();
+
+
+// set whether "Set defaults" button should be enabled
+boolean enableSetDefaults = !( userUpdates &amp;&amp; !allowUserAppdirPath &amp;&amp; userAppdirPath.length() == 0 &amp;&amp; !installerUpdates );
+component.setEnabled(enableSetDefaults);</initScript>
+                      </formComponent>
+                      <formComponent name="BOTHSPACE: No updates warning" id="2988" customizedId="SS_NO_UPDATES_WARNING" beanClass="com.install4j.runtime.beans.formcomponents.LabelComponent" insetLeft="16">
+                        <serializedBean>
+                          <property name="labelIconFile">
+                            <object class="com.install4j.api.beans.ExternalFile">
+                              <string>${compiler:JALVIEW_DIR}/${compiler:INSTALL4J_UTILS_DIR}/warning.png</string>
+                            </object>
+                          </property>
+                          <property name="labelText" type="string">No automatic updates will occur when ${compiler:JALVIEW_APPLICATION_NAME} is launched</property>
+                        </serializedBean>
+                        <visibilityScript>!( context.getBooleanVariable("allowUserDefaultAppdirUpdates") || context.getBooleanVariable("allowInstallerAppdirUpdates") )</visibilityScript>
+                      </formComponent>
+                      <formComponent name="SYSTEMSPACE: Invalid user-space path warning" id="3027" customizedId="SS_INVALID_USER_APPDIR_PATH_WARNING" beanClass="com.install4j.runtime.beans.formcomponents.LabelComponent" insetLeft="16">
+                        <serializedBean>
+                          <property name="labelIconFile">
+                            <object class="com.install4j.api.beans.ExternalFile">
+                              <string>${compiler:JALVIEW_DIR}/${compiler:INSTALL4J_UTILS_DIR}/warning.png</string>
+                            </object>
+                          </property>
+                          <property name="labelText" type="string">The user-space path should contain one of "~${installer:sys.fileSeparator}" (at the start), "%u" or "%h"</property>
+                        </serializedBean>
+                        <visibilityScript>String userAppdirPath = (String) context.getVariable("userAppdirPath");
+
+if (userAppdirPath == null) {
+  return false;
+}
+
+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 );
+
+return context.getBooleanVariable("allowUserDefaultAppdirUpdates") &amp;&amp; context.getBooleanVariable("allowSetUserAppdirPath") &amp;&amp; showInvalidPathWarning;</visibilityScript>
+                      </formComponent>
+                    </beans>
+                  </group>
+                </beans>
+              </group>
             </formComponents>
           </screen>
-          <screen id="15" beanClass="com.install4j.runtime.beans.screens.InstallationScreen" rollbackBarrier="true" rollbackBarrierExitCode="0">
+          <screen id="15" customizedId="INSTALLATION" beanClass="com.install4j.runtime.beans.screens.InstallationScreen" rollbackBarrier="true" rollbackBarrierExitCode="0">
             <actions>
               <action id="17" beanClass="com.install4j.runtime.beans.actions.InstallFilesAction" rollbackBarrierExitCode="0" failureStrategy="quit" errorMessage="${i18n:FileCorrupted}" />
               <action name="Create program group (RELEASE)" id="18" customizedId="PROGRAM_GROUP_RELEASE" beanClass="com.install4j.runtime.beans.actions.desktop.CreateProgramGroupAction" actionElevationType="elevated" rollbackBarrierExitCode="0">
@@ -694,7 +1705,7 @@ return console.askOkCancel(message, true);
                   <property name="itemName" type="string">${compiler:sys.fullName} ${compiler:sys.version}</property>
                 </serializedBean>
               </action>
-              <group name="File Associations" id="2251" beanClass="com.install4j.runtime.beans.groups.ActionGroup">
+              <group name="File Associations" id="2251" customizedId="FA_SCREEN" beanClass="com.install4j.runtime.beans.groups.ActionGroup">
                 <beans>
                   <action id="2253" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
@@ -728,11 +1739,23 @@ return console.askOkCancel(message, true);
                   </action>
                   <action id="2541" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
-                      <property name="percentValue" type="int" value="100" />
+                      <property name="percentValue" type="int" value="99" />
                     </serializedBean>
                   </action>
                 </beans>
               </group>
+              <action id="3017" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                <serializedBean>
+                  <property name="statusMessage" type="string">${i18n:FinishedHeadingLabel(${compiler:JALVIEW_APPLICATION_NAME})}</property>
+                  <property name="useDetail" type="boolean" value="true" />
+                  <property name="useStatus" type="boolean" value="true" />
+                </serializedBean>
+              </action>
+              <action id="3018" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                <serializedBean>
+                  <property name="progressChangeType" type="enum" class="com.install4j.runtime.beans.actions.control.ProgressChangeType" value="SET_INDETERMINATE" />
+                </serializedBean>
+              </action>
               <group name="Register URL Handlers" id="2957" beanClass="com.install4j.runtime.beans.groups.ActionGroup">
                 <beans>
                   <action id="2350" beanClass="com.install4j.runtime.beans.actions.desktop.UrlHandlerAction" actionElevationType="elevated" rollbackBarrierExitCode="0">
@@ -776,7 +1799,7 @@ return console.askOkCancel(message, true);
                   </property>
                 </serializedBean>
                 <beans>
-                  <action name="USERSPACE: Create start menu item" id="2916" beanClass="com.install4j.runtime.beans.actions.desktop.CreateStartMenuEntryAction" actionElevationType="elevated" rollbackBarrierExitCode="0">
+                  <action name="USERSPACE: Create Start Menu item" id="2916" beanClass="com.install4j.runtime.beans.actions.desktop.CreateStartMenuEntryAction" actionElevationType="elevated" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="allUsers" type="boolean" value="false" />
                       <property name="categories" type="string">${compiler:APPLICATION_CATEGORIES}</property>
@@ -798,7 +1821,7 @@ return console.askOkCancel(message, true);
                         </object>
                       </property>
                     </serializedBean>
-                    <condition>!Util.hasFullAdminRights() &amp;&amp; !context.getBooleanVariable("sys.programGroupDisabled")</condition>
+                    <condition>!context.getBooleanVariable("sys.programGroupDisabled")</condition>
                   </action>
                   <action name="USERSPACE: Add a desktop link" id="2917" beanClass="com.install4j.runtime.beans.actions.desktop.CreateDesktopLinkAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make desktop link">
                     <serializedBean>
@@ -821,7 +1844,7 @@ return console.askOkCancel(message, true);
                         </object>
                       </property>
                     </serializedBean>
-                    <condition>!Util.hasFullAdminRights() &amp;&amp; context.getBooleanVariable("createDesktopLinkAction")</condition>
+                    <condition>context.getBooleanVariable("createDesktopLinkAction")</condition>
                   </action>
                   <action name="USERSPACE: Add an executable to the dock (macOS)" id="2918" beanClass="com.install4j.runtime.beans.actions.desktop.AddToDockAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
@@ -831,9 +1854,7 @@ return console.askOkCancel(message, true);
                         </object>
                       </property>
                     </serializedBean>
-                    <condition>!Util.hasFullAdminRights()
-&amp;&amp;
-context.getBooleanVariable("addToDockAction")</condition>
+                    <condition>context.getBooleanVariable("addToDockAction")</condition>
                   </action>
                   <action name="USERSPACE: Add Jalview bin to the user's path (Windows)" id="2919" beanClass="com.install4j.runtime.beans.actions.misc.ModifyEnvironmentVariableAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not add &quot;${installer:sys.contentDir}\${compiler:WRAPPER_SCRIPT_BIN_DIR}&quot; to the Path environment variable">
                     <serializedBean>
@@ -841,13 +1862,53 @@ context.getBooleanVariable("addToDockAction")</condition>
                       <property name="value" type="string">${installer:sys.contentDir}\${compiler:WRAPPER_SCRIPT_BIN_DIR}</property>
                       <property name="variableName" type="string">Path</property>
                     </serializedBean>
-                    <condition>!Util.hasFullAdminRights() &amp;&amp; context.getBooleanVariable("appendToPathAction")</condition>
+                    <condition>context.getBooleanVariable("appendToPathAction")</condition>
                   </action>
-                  <action name="USERSPACE: Create macOS symbolic link to jalview in user's local bin" id="2920" beanClass="com.install4j.runtime.beans.actions.files.CreateSymlinkAction" rollbackBarrierExitCode="0" errorMessage="Could not make a ${compiler:WRAPPER_LINK} symbolic link in ~/${installer:unixBinDir}">
+                  <action name="USERSPACE: Create macOS symbolic link to jalview.sh" id="2920" beanClass="com.install4j.runtime.beans.actions.files.CreateSymlinkAction" rollbackBarrierExitCode="0" errorMessage="Could not make a ${compiler:WRAPPER_LINK} symbolic link in ~/${installer:MacOSDir}">
                     <serializedBean>
                       <property name="file">
                         <object class="java.io.File">
-                          <string>${installer:macWrapperLinkLocation}</string>
+                          <string>../Resources/app/${compiler:WRAPPER_SCRIPT_BIN_DIR}/${compiler:BASH_WRAPPER_SCRIPT}</string>
+                        </object>
+                      </property>
+                      <property name="linkFile">
+                        <object class="java.io.File">
+                          <string>${installer:MacOSDir}/${compiler:WRAPPER_LINK}</string>
+                        </object>
+                      </property>
+                    </serializedBean>
+                    <condition>Util.isMacOS() &amp;&amp;
+(
+  context.getBooleanVariable("makeSymbolicLinkAction")
+  &amp;&amp; context.getVariable("unixBinDir") != null
+  &amp;&amp; context.getVariable("MacOSDir") != null
+)</condition>
+                  </action>
+                  <action name="USERSPACE: Create macOS symbolic link to update.sh" id="3052" beanClass="com.install4j.runtime.beans.actions.files.CreateSymlinkAction" rollbackBarrierExitCode="0" errorMessage="Could not make a ${compiler:WRAPPER_LINK} symbolic link in ~/${installer:MacOSDir}">
+                    <serializedBean>
+                      <property name="file">
+                        <object class="java.io.File">
+                          <string>../Resources/app/${compiler:WRAPPER_SCRIPT_BIN_DIR}/${compiler:BASH_UPDATE_SCRIPT}</string>
+                        </object>
+                      </property>
+                      <property name="linkFile">
+                        <object class="java.io.File">
+                          <string>${installer:MacOSDir}/update_${compiler:WRAPPER_LINK}</string>
+                        </object>
+                      </property>
+                    </serializedBean>
+                    <condition>Util.isMacOS() &amp;&amp;
+(
+  context.getBooleanVariable("makeSymbolicLinkAction")
+  &amp;&amp; context.getVariable("unixBinDir") != null
+  &amp;&amp; context.getVariable("MacOSDir") != null
+)</condition>
+                  </action>
+                  <action name="USERSPACE: Create macOS symbolic link to jalview in user's local bin" id="3048" beanClass="com.install4j.runtime.beans.actions.files.CreateSymlinkAction" rollbackBarrierExitCode="0" errorMessage="Could not make a ${compiler:WRAPPER_LINK} symbolic link in ~/${installer:unixBinDir}">
+                    <serializedBean>
+                      <property name="file">
+                        <object class="java.io.File">
+                          <string>${installer:MacOSDir}/${compiler:WRAPPER_LINK}</string>
                         </object>
                       </property>
                       <property name="linkFile">
@@ -856,12 +1917,31 @@ context.getBooleanVariable("addToDockAction")</condition>
                         </object>
                       </property>
                     </serializedBean>
-                    <condition>Util.isMacOS() &amp;&amp; !Util.hasFullAdminRights() // Admin on macOS will add path to /etc/paths.d
-&amp;&amp;
+                    <condition>Util.isMacOS() &amp;&amp;
 (
   context.getBooleanVariable("makeSymbolicLinkAction")
   &amp;&amp; context.getVariable("unixBinDir") != null
-  &amp;&amp; context.getVariable("macWrapperLinkLocation") != null
+  &amp;&amp; context.getVariable("MacOSDir") != null
+)</condition>
+                  </action>
+                  <action name="USERSPACE: Create macOS symbolic link to jalview_update in user's local bin" id="3049" beanClass="com.install4j.runtime.beans.actions.files.CreateSymlinkAction" rollbackBarrierExitCode="0" errorMessage="Could not make a ${compiler:WRAPPER_LINK} symbolic link in ~/${installer:unixBinDir}">
+                    <serializedBean>
+                      <property name="file">
+                        <object class="java.io.File">
+                          <string>${installer:MacOSDir}/update_${compiler:WRAPPER_LINK}</string>
+                        </object>
+                      </property>
+                      <property name="linkFile">
+                        <object class="java.io.File">
+                          <string>${installer:unixBinDir}/update_${compiler:WRAPPER_LINK}</string>
+                        </object>
+                      </property>
+                    </serializedBean>
+                    <condition>Util.isMacOS() &amp;&amp;
+(
+  context.getBooleanVariable("makeSymbolicLinkAction")
+  &amp;&amp; context.getVariable("unixBinDir") != null
+  &amp;&amp; context.getVariable("MacOSDir") != null
 )</condition>
                   </action>
                 </beans>
@@ -875,7 +1955,7 @@ context.getBooleanVariable("addToDockAction")</condition>
                   </property>
                 </serializedBean>
                 <beans>
-                  <action name="SYSTEMSPACE: Create start menu item" id="2950" beanClass="com.install4j.runtime.beans.actions.desktop.CreateStartMenuEntryAction" actionElevationType="elevated" rollbackBarrierExitCode="0">
+                  <action name="SYSTEMSPACE: Create Start Menu item" id="2950" beanClass="com.install4j.runtime.beans.actions.desktop.CreateStartMenuEntryAction" actionElevationType="elevated" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="categories" type="string">${compiler:APPLICATION_CATEGORIES}</property>
                       <property name="entryName" type="string">${compiler:JALVIEW_APPLICATION_NAME}</property>
@@ -922,7 +2002,7 @@ context.getBooleanVariable("addToDockAction")</condition>
                     </serializedBean>
                     <condition>context.getBooleanVariable("createDesktopLinkAction")</condition>
                   </action>
-                  <action name="SYSTEMSPACE: Add Jalview bin to the user's path (Windows)" id="2952" beanClass="com.install4j.runtime.beans.actions.misc.ModifyEnvironmentVariableAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not add &quot;${installer:sys.contentDir}\${compiler:WRAPPER_SCRIPT_BIN_DIR}&quot; to the Path environment variable">
+                  <action name="SYSTEMSPACE: Add Jalview bin to the system path (Windows)" id="2952" beanClass="com.install4j.runtime.beans.actions.misc.ModifyEnvironmentVariableAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not add &quot;${installer:sys.contentDir}\${compiler:WRAPPER_SCRIPT_BIN_DIR}&quot; to the Path environment variable">
                     <serializedBean>
                       <property name="type" type="enum" class="com.install4j.runtime.beans.actions.misc.ModifyStringType" value="APPEND" />
                       <property name="userSpecific" type="boolean" value="false" />
@@ -931,14 +2011,27 @@ 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");
+String hash = getCanonicalFullPathToDirectoryHash(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>
@@ -946,7 +2039,7 @@ context.getBooleanVariable("addToDockAction")</condition>
               </group>
               <group name="BOTHSPACE: Actions" id="2956" beanClass="com.install4j.runtime.beans.groups.ActionGroup" actionElevationType="elevated">
                 <beans>
-                  <action name="BOTHSPACE: Create Linux/Unix user symbolic link to jalview.sh in system bin" id="2955" beanClass="com.install4j.runtime.beans.actions.files.CreateSymlinkAction" rollbackBarrierExitCode="0" errorMessage="Could not make a ${compiler:WRAPPER_LINK} symbolic link in ~/${installer:unixBinDir}">
+                  <action name="BOTHSPACE: Create Linux/Unix user symbolic link to jalview.sh in system/user bin" id="2955" beanClass="com.install4j.runtime.beans.actions.files.CreateSymlinkAction" rollbackBarrierExitCode="0" errorMessage="Could not make a ${compiler:WRAPPER_LINK} symbolic link in ~/${installer:unixBinDir}">
                     <serializedBean>
                       <property name="file">
                         <object class="java.io.File">
@@ -965,6 +2058,177 @@ context.getBooleanVariable("addToDockAction")</condition>
                   </action>
                 </beans>
               </group>
+              <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>
+                      <property name="script">
+                        <object class="com.install4j.api.beans.ScriptProperty">
+                          <property name="value" type="string">boolean advanced = context.getBooleanVariable("advancedOptions");
+boolean allowUser = context.getBooleanVariable("allowUserDefaultAppdirUpdates");
+boolean consoleDisableUserAppdir = context.getBooleanVariable("consoleDisableUserAppdir");
+if (consoleDisableUserAppdir) {
+    return true;
+}
+return advanced ? !allowUser : false;</property>
+                        </object>
+                      </property>
+                      <property name="variableName" type="string">disableUserDefaultAppdirUpdates</property>
+                    </serializedBean>
+                  </action>
+                  <action name="Set setUserAppdirPath" id="3025" 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">boolean advanced = context.getBooleanVariable("advancedOptions");
+boolean allowUser = context.getBooleanVariable("allowUserDefaultAppdirUpdates");
+boolean allowSetUserAppdirPath = context.getBooleanVariable("allowSetUserAppdirPath");
+String userAppdirPath = (String)context.getVariable("userAppdirPath");
+boolean saneValue = userAppdirPath != null &amp;&amp; userAppdirPath.length() &gt; 0;
+boolean consoleAllowUserAppdirPath = context.getBooleanVariable("consoleAllowUserAppdirPath");
+if (consoleAllowUserAppdirPath &amp;&amp; saneValue) {
+    return userAppdirPath;
+}
+return advanced &amp;&amp; allowUser &amp;&amp; allowSetUserAppdirPath &amp;&amp; saneValue ? userAppdirPath : null;</property>
+                        </object>
+                      </property>
+                      <property name="variableName" type="string">setUserAppdirPath</property>
+                    </serializedBean>
+                  </action>
+                  <action name="Set disableUpdates" id="3012" 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">boolean advanced = context.getBooleanVariable("advancedOptions");
+boolean allowUser = context.getBooleanVariable("allowUserDefaultAppdirUpdates");
+boolean allowInstaller = context.getBooleanVariable("allowInstallerAppdirUpdates");
+boolean consoleDisableAllUpdates = context.getBooleanVariable("consoleDisableAllUpdates");
+if (consoleDisableAllUpdates) {
+    return true;
+}
+return advanced ? !( allowUser || allowInstaller ) : false;</property>
+                        </object>
+                      </property>
+                      <property name="variableName" type="string">disableUpdates</property>
+                    </serializedBean>
+                  </action>
+                  <action name="Set installerFilename" id="3043" 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 file = (String)context.getVariable("sys.mediaFile");
+int i = file.lastIndexOf(File.separator);
+return file.substring(i+1);</property>
+                        </object>
+                      </property>
+                      <property name="variableName" type="string">installerFilename</property>
+                    </serializedBean>
+                  </action>
+                  <action name="Set installDateTime" id="3044" 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 date = (String)context.getVariable("sys.date");
+String time = (String)context.getVariable("sys.time");
+StringBuilder sb = new StringBuilder();
+sb.append(date.substring(0,4));
+sb.append("-");
+sb.append(date.substring(4,6));
+sb.append("-");
+sb.append(date.substring(6,8));
+sb.append(" ");
+sb.append(time.substring(0,2));
+sb.append(":");
+sb.append(time.substring(2,4));
+sb.append(":");
+sb.append(time.substring(4,6));
+return sb.toString();
+</property>
+                        </object>
+                      </property>
+                      <property name="variableName" type="string">installDateTime</property>
+                    </serializedBean>
+                  </action>
+                  <action name="Initial comment" id="3010" beanClass="com.install4j.runtime.beans.actions.misc.AddVmOptionsAction" actionElevationType="elevated" rollbackBarrierExitCode="0">
+                    <serializedBean>
+                      <property name="launcherId" type="string">2823</property>
+                      <property name="vmOptions" type="array" elementType="string" length="1">
+                        <element index="0"># Jalview options added by ${installer:installerFilename} at ${installer:installDateTime}</element>
+                      </property>
+                    </serializedBean>
+                  </action>
+                  <action name="Allow user-space updates" id="2996" beanClass="com.install4j.runtime.beans.actions.misc.AddVmOptionsAction" actionElevationType="elevated" rollbackBarrierExitCode="0">
+                    <serializedBean>
+                      <property name="launcherId" type="string">2823</property>
+                      <property name="vmOptions" type="array" elementType="string" length="3">
+                        <element index="0"># </element>
+                        <element index="1"># Uncomment the following line to disable user-space updates</element>
+                        <element index="2">#-Dnouserdefaultappdir=true</element>
+                      </property>
+                    </serializedBean>
+                    <condition>!context.getBooleanVariable("disableUserDefaultAppdirUpdates")</condition>
+                  </action>
+                  <action name="Disable user-space updates" id="2997" beanClass="com.install4j.runtime.beans.actions.misc.AddVmOptionsAction" actionElevationType="elevated" rollbackBarrierExitCode="0">
+                    <serializedBean>
+                      <property name="launcherId" type="string">2823</property>
+                      <property name="vmOptions" type="array" elementType="string" length="3">
+                        <element index="0"># </element>
+                        <element index="1"># Comment out the following line to allow user-space updates</element>
+                        <element index="2">-Dnouserdefaultappdir=true</element>
+                      </property>
+                    </serializedBean>
+                    <condition>context.getBooleanVariable("disableUserDefaultAppdirUpdates")</condition>
+                  </action>
+                  <action name="Set setUserAppdirPath property" id="3026" beanClass="com.install4j.runtime.beans.actions.misc.AddVmOptionsAction" actionElevationType="elevated" rollbackBarrierExitCode="0">
+                    <serializedBean>
+                      <property name="launcherId" type="string">2823</property>
+                      <property name="vmOptions" type="array" elementType="string" length="5">
+                        <element index="0"># </element>
+                        <element index="1"># The below line sets a custom path for user-space updates -- use with caution.</element>
+                        <element index="2"># A leading ~/ or %h anywhere will be substituted with the user's home path, and %u by the username.</element>
+                        <element index="3"># If unset, the default is ${installer:userDefaultAppdirBase} for ${installer:osName}</element>
+                        <element index="4">-Dsetuserappdirpath=${installer:setUserAppdirPath}</element>
+                      </property>
+                    </serializedBean>
+                    <condition>(String)context.getVariable("setUserAppdirPath") != null</condition>
+                  </action>
+                  <action name="Don't set setUserAppdirPath property" id="3037" beanClass="com.install4j.runtime.beans.actions.misc.AddVmOptionsAction" actionElevationType="elevated" rollbackBarrierExitCode="0">
+                    <serializedBean>
+                      <property name="launcherId" type="string">2823</property>
+                      <property name="vmOptions" type="array" elementType="string" length="5">
+                        <element index="0"># </element>
+                        <element index="1"># Uncomment the below line to set a custom path for user-space updates -- use with caution.</element>
+                        <element index="2"># A leading ~/ or %h anywhere will be substituted with the user's home path, and %u by the username.</element>
+                        <element index="3"># If not set, the default is ${installer:userDefaultAppdirBase} for ${installer:osName}</element>
+                        <element index="4">#-Dsetuserappdirpath=/tmp/jalview/%u</element>
+                      </property>
+                    </serializedBean>
+                    <condition>(String)context.getVariable("setUserAppdirPath") == null</condition>
+                  </action>
+                  <action name="Allow updates" id="2998" beanClass="com.install4j.runtime.beans.actions.misc.AddVmOptionsAction" actionElevationType="elevated" rollbackBarrierExitCode="0">
+                    <serializedBean>
+                      <property name="launcherId" type="string">2823</property>
+                      <property name="vmOptions" type="array" elementType="string" length="3">
+                        <element index="0"># </element>
+                        <element index="1"># Uncomment the following line to also disable all updates</element>
+                        <element index="2">#-Dsilent=noupdate </element>
+                      </property>
+                    </serializedBean>
+                    <condition>!context.getBooleanVariable("disableUpdates")</condition>
+                  </action>
+                  <action name="Disable updates" id="2999" beanClass="com.install4j.runtime.beans.actions.misc.AddVmOptionsAction" actionElevationType="elevated" rollbackBarrierExitCode="0">
+                    <serializedBean>
+                      <property name="launcherId" type="string">2823</property>
+                      <property name="vmOptions" type="array" elementType="string" length="3">
+                        <element index="0"># </element>
+                        <element index="1"># Comment out the following line to enable updates</element>
+                        <element index="2">-Dsilent=noupdate</element>
+                      </property>
+                    </serializedBean>
+                    <condition>context.getBooleanVariable("disableUpdates")</condition>
+                  </action>
+                </beans>
+              </group>
               <group name="Java bin symlinks" id="2944" beanClass="com.install4j.runtime.beans.groups.ActionGroup">
                 <beans>
                   <action name="macOS/Linux Jalview-&gt;java symlink" id="2942" beanClass="com.install4j.runtime.beans.actions.files.CreateSymlinkAction" rollbackBarrierExitCode="0" errorMessage="Could not make symlink to wrapper script">
@@ -981,7 +2245,7 @@ context.getBooleanVariable("addToDockAction")</condition>
                       </property>
                       <property name="removeOnUninstall" type="boolean" value="false" />
                     </serializedBean>
-                    <condition>Util.isLinux() || Util.isMacOS()</condition>
+                    <condition>Util.isLinux() || ( Util.isMacOS() &amp;&amp; !Util.isUnixInstaller() )</condition>
                   </action>
                   <action name="macOS/Linux Jalview Appname-&gt;java symlink" id="2943" beanClass="com.install4j.runtime.beans.actions.files.CreateSymlinkAction" rollbackBarrierExitCode="0" errorMessage="Could not make symlink to wrapper script">
                     <serializedBean>
@@ -997,14 +2261,14 @@ context.getBooleanVariable("addToDockAction")</condition>
                       </property>
                       <property name="removeOnUninstall" type="boolean" value="false" />
                     </serializedBean>
-                    <condition>(Util.isLinux() || Util.isMacOS())
+                    <condition>( Util.isLinux() || ( Util.isMacOS() &amp;&amp; !Util.isUnixInstaller() ) )
 &amp;&amp; !((String)context.getCompilerVariable("JALVIEW_APPLICATION_NAME")).equals((String)context.getCompilerVariable("JALVIEW_NAME"))</condition>
                   </action>
                 </beans>
               </group>
               <group name="Jalview bin symlink" id="2946" beanClass="com.install4j.runtime.beans.groups.ActionGroup">
                 <beans>
-                  <action name="Linux/Unix Jalview bin dir symlink" id="2945" beanClass="com.install4j.runtime.beans.actions.files.CreateSymlinkAction" rollbackBarrierExitCode="0" errorMessage="Could not make symlink to wrapper script">
+                  <action name="Linux/Unix jalview.sh bin dir symlink" id="2945" beanClass="com.install4j.runtime.beans.actions.files.CreateSymlinkAction" rollbackBarrierExitCode="0" errorMessage="Could not make symlink to wrapper script">
                     <serializedBean>
                       <property name="file">
                         <object class="java.io.File">
@@ -1019,48 +2283,41 @@ context.getBooleanVariable("addToDockAction")</condition>
                     </serializedBean>
                     <condition>Util.isLinux() || Util.isUnixInstaller() || Util.isMacOS()</condition>
                   </action>
-                </beans>
-              </group>
-              <group name="Windows scripts" id="2949" beanClass="com.install4j.runtime.beans.groups.ActionGroup">
-                <beans>
-                  <action name="Windows copy BAT file" id="2947" beanClass="com.install4j.runtime.beans.actions.files.CopyFileAction" rollbackBarrierExitCode="0">
+                  <action name="Linux/Unix update.sh bin dir symlink" id="3079" beanClass="com.install4j.runtime.beans.actions.files.CreateSymlinkAction" rollbackBarrierExitCode="0" errorMessage="Could not make symlink to wrapper script">
                     <serializedBean>
-                      <property name="destinationFile">
+                      <property name="file">
                         <object class="java.io.File">
-                          <string>${compiler:WRAPPER_SCRIPT_BIN_DIR}/${compiler:WRAPPER_LINK}.bat</string>
+                          <string>${compiler:WRAPPER_SCRIPT_BIN_DIR}/${compiler:BASH_UPDATE_SCRIPT}</string>
                         </object>
                       </property>
-                      <property name="files" type="array" class="java.io.File" length="1">
-                        <element index="0">
-                          <object class="java.io.File">
-                            <string>${compiler:WRAPPER_SCRIPT_BIN_DIR}/${compiler:BATCH_WRAPPER_SCRIPT}</string>
-                          </object>
-                        </element>
-                      </property>
-                    </serializedBean>
-                    <condition>Util.isWindows() &amp;&amp; !(((String)context.getCompilerVariable("WRAPPER_LINK")+".bat").equals((String)context.getCompilerVariable("BATCH_WRAPPER_SCRIPT")))</condition>
-                  </action>
-                  <action name="Windows copy PS1 file" id="2948" beanClass="com.install4j.runtime.beans.actions.files.CopyFileAction" rollbackBarrierExitCode="0">
-                    <serializedBean>
-                      <property name="destinationFile">
+                      <property name="linkFile">
                         <object class="java.io.File">
-                          <string>${compiler:WRAPPER_SCRIPT_BIN_DIR}/${compiler:WRAPPER_LINK}.ps1</string>
+                          <string>${compiler:WRAPPER_SCRIPT_BIN_DIR}/${compiler:WRAPPER_LINK}_update</string>
                         </object>
                       </property>
-                      <property name="files" type="array" class="java.io.File" length="1">
-                        <element index="0">
-                          <object class="java.io.File">
-                            <string>${compiler:WRAPPER_SCRIPT_BIN_DIR}/${compiler:POWERSHELL_WRAPPER_SCRIPT}</string>
-                          </object>
-                        </element>
-                      </property>
                     </serializedBean>
-                    <condition>Util.isWindows() &amp;&amp; !(((String)context.getCompilerVariable("WRAPPER_LINK")+".ps1").equals((String)context.getCompilerVariable("POWERSHELL_WRAPPER_SCRIPT")))</condition>
+                    <condition>Util.isLinux() || Util.isUnixInstaller() || Util.isMacOS()</condition>
                   </action>
                 </beans>
               </group>
+              <action id="3020" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                <serializedBean>
+                  <property name="percentValue" type="int" value="100" />
+                </serializedBean>
+              </action>
+              <action id="3065" beanClass="com.install4j.runtime.beans.actions.control.SleepAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                <serializedBean>
+                  <property name="sleepTimeMs" type="int" value="250" />
+                </serializedBean>
+              </action>
             </actions>
             <formComponents>
+              <formComponent name="Administrator mode label" id="3061" beanClass="com.install4j.runtime.beans.formcomponents.MultilineHtmlLabelComponent" insetBottom="16">
+                <serializedBean>
+                  <property name="labelHtml" type="string">&lt;strong&gt;Administrator mode&lt;/strong&gt;</property>
+                </serializedBean>
+                <visibilityScript>context.getBooleanVariable("isAdmin")</visibilityScript>
+              </formComponent>
               <formComponent id="16" beanClass="com.install4j.runtime.beans.formcomponents.ProgressComponent">
                 <serializedBean>
                   <property name="initialStatusMessage" type="string">${i18n:WizardPreparing}</property>
@@ -1070,6 +2327,12 @@ context.getBooleanVariable("addToDockAction")</condition>
           </screen>
           <screen id="20" beanClass="com.install4j.runtime.beans.screens.FinishedScreen" rollbackBarrierExitCode="0" finishScreen="true">
             <formComponents>
+              <formComponent name="Administrator mode label" id="3060" beanClass="com.install4j.runtime.beans.formcomponents.MultilineHtmlLabelComponent" insetBottom="16">
+                <serializedBean>
+                  <property name="labelHtml" type="string">&lt;strong&gt;Administrator mode&lt;/strong&gt;</property>
+                </serializedBean>
+                <visibilityScript>context.getBooleanVariable("isAdmin")</visibilityScript>
+              </formComponent>
               <formComponent id="21" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" insetBottom="10">
                 <serializedBean>
                   <property name="labelText" type="string">${form:finishedMessage}</property>
@@ -1468,7 +2731,7 @@ return console.askYesNo(message, true);
 ( Util.isLinux()
   || Util.isUnixInstaller()
   || ( Util.isMacOS()
-       &amp;&amp; context.getVariable("macWrapperLinkLocation") != null
+       &amp;&amp; context.getVariable("MacOSDir") != null
      )
  )
  &amp;&amp; context.getVariable("unixBinDir") != null</visibilityScript>
@@ -1514,7 +2777,7 @@ ${compiler:JALVIEW_APPLICATION_NAME} will now launch.</property>
             </serializedBean>
             <beans>
               <formComponent id="38" beanClass="com.install4j.runtime.beans.styles.ContentComponent" insetTop="10" insetLeft="20" insetBottom="10" insetRight="20" />
-              <formComponent name="Watermark" id="39" beanClass="com.install4j.runtime.beans.formcomponents.SeparatorComponent" insetTop="0" insetLeft="5" insetBottom="0" useExternalParametrization="true" externalParametrizationName="${compiler:JALVIEW_APPLICATION_NAME}" externalParametrizationMode="include">
+              <formComponent name="Watermark" id="39" beanClass="com.install4j.runtime.beans.formcomponents.SeparatorComponent" insetTop="0" insetLeft="5" insetBottom="0" useExternalParametrization="true" externalParametrizationMode="include">
                 <serializedBean>
                   <property name="enabledTitleText" type="boolean" value="false" />
                 </serializedBean>
@@ -1765,9 +3028,13 @@ ${compiler:JALVIEW_APPLICATION_NAME} will now launch.</property>
         <file name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/Resources/Jalview-File.icns" file="${compiler:JALVIEW_DIR}/${compiler:INSTALL4J_UTILS_DIR}/Jalview-File.icns" />
         <file name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/Resources/jvl_file.icns" file="${compiler:JALVIEW_DIR}/${compiler:INSTALL4J_UTILS_DIR}/jvl_file.icns" />
         <symlink name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/MacOS/${compiler:WRAPPER_LINK}" target="../Resources/app/${compiler:WRAPPER_SCRIPT_BIN_DIR}/${compiler:BASH_WRAPPER_SCRIPT}" />
+        <symlink name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/MacOS/${compiler:WRAPPER_LINK}_update" target="../Resources/app/${compiler:WRAPPER_SCRIPT_BIN_DIR}/${compiler:BASH_UPDATE_SCRIPT}" />
         <symlink name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/Resources/app/jre/Contents/Home/bin/${compiler:JALVIEW_APPLICATION_NAME}" target="java" />
         <symlink name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/Resources/app/jre/Contents/Home/bin/${compiler:JALVIEW_NAME}" target="java" />
         <file name=".VolumeIcon.icns" file="${compiler:JALVIEW_DIR}/${compiler:MACOSARCHIVE_VOLUMEICON}" />
+        <file name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/vmoptions.txt" file="${compiler:JALVIEW_DIR}/${compiler:BUILD_DIR}/tmp/default.vmoptions.X64" />
+        <file name=".jalview/build_properties" file="${compiler:JALVIEW_DIR}/build/resources/resources_build/.build_properties" />
+        <file name=".jalview/channel.props" file="${compiler:JALVIEW_DIR}/build/resources/resources_build/channel.props" />
       </topLevelFiles>
     </macosArchive>
     <macosArchive name="macOS (Apple Silicon) Disk Image" id="2796" customizedId="MACOS-AARCH64-DMG" mediaFileName="${compiler:MACOSARCHIVE_AARCH64_DMG_FILENAME}" volumeName="${compiler:MACOSARCHIVE_AARCH64_NAME}" architecture="aarch64" launcherId="2823" setupAppId="2746">
@@ -1786,9 +3053,13 @@ ${compiler:JALVIEW_APPLICATION_NAME} will now launch.</property>
         <file name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/Resources/Jalview-File.icns" file="${compiler:JALVIEW_DIR}/${compiler:INSTALL4J_UTILS_DIR}/Jalview-File.icns" />
         <file name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/Resources/jvl_file.icns" file="${compiler:JALVIEW_DIR}/${compiler:INSTALL4J_UTILS_DIR}/jvl_file.icns" />
         <symlink name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/MacOS/${compiler:WRAPPER_LINK}" target="../Resources/app/${compiler:WRAPPER_SCRIPT_BIN_DIR}/${compiler:BASH_WRAPPER_SCRIPT}" />
+        <symlink name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/MacOS/${compiler:WRAPPER_LINK}_update" target="../Resources/app/${compiler:WRAPPER_SCRIPT_BIN_DIR}/${compiler:BASH_UPDATE_SCRIPT}" />
         <symlink name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/Resources/app/jre/Contents/Home/bin/${compiler:JALVIEW_APPLICATION_NAME}" target="java" />
         <symlink name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/Resources/app/jre/Contents/Home/bin/${compiler:JALVIEW_NAME}" target="java" />
         <file name=".VolumeIcon.icns" file="${compiler:JALVIEW_DIR}/${compiler:MACOSARCHIVE_VOLUMEICON}" />
+        <file name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/vmoptions.txt" file="${compiler:JALVIEW_DIR}/${compiler:BUILD_DIR}/tmp/default.vmoptions.AARCH64" />
+        <file name=".jalview/build_properties" file="${compiler:JALVIEW_DIR}/build/resources/resources_build/.build_properties" />
+        <file name=".jalview/channel.props" file="${compiler:JALVIEW_DIR}/build/resources/resources_build/channel.props" />
       </topLevelFiles>
     </macosArchive>
     <unixInstaller name="Linux x64 Shell Installer" id="1595" customizedId="LINUX-X64-SH" mediaFileName="${compiler:UNIX_APPLICATION_FOLDER}-${compiler:JALVIEW_VERSION}-linux-x64-java_${compiler:JAVA_INTEGER_VERSION}" installDir="${compiler:UNIX_APPLICATION_FOLDER}">
@@ -1802,7 +3073,7 @@ ${compiler:JALVIEW_APPLICATION_NAME} will now launch.</property>
       </exclude>
       <variables>
         <variable name="variable" value="720" />
-        <variable name="WIZARD_HEIGHT" value="540" />
+        <variable name="WIZARD_HEIGHT" value="800" />
       </variables>
       <jreBundle jreBundleSource="preCreated" includedJre="${compiler:LINUX_X64_JAVA_VM_TGZ}" manualJreEntry="true" />
     </unixInstaller>
@@ -1817,7 +3088,7 @@ ${compiler:JALVIEW_APPLICATION_NAME} will now launch.</property>
       </exclude>
       <variables>
         <variable name="variable" value="720" />
-        <variable name="WIZARD_HEIGHT" value="540" />
+        <variable name="WIZARD_HEIGHT" value="800" />
       </variables>
       <jreBundle jreBundleSource="preCreated" includedJre="${compiler:LINUX_AARCH64_JAVA_VM_TGZ}" manualJreEntry="true" />
     </unixInstaller>
@@ -1844,13 +3115,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="3042" />
   </buildIds>
 </install4j>
diff --git a/utils/install4j/macos-install-jalview.sh b/utils/install4j/macos-install-jalview.sh
new file mode 100755 (executable)
index 0000000..60dc70c
--- /dev/null
@@ -0,0 +1,414 @@
+#!/usr/bin/env bash
+
+usage() {
+  echo "Usage: $( basename $0 ) [-h] [ [-d] [-m arch] [-c channel] | [-i filename] ] [-a folder] [-u] [-U] [-S] [-P] [-R]"
+  echo ""
+  echo "  This script downloads and installs Jalview in macOS, or installs from a previously downloaded Jalview DMG file."
+  echo "  One of -d or -i should be specified, whichever comes last is used."
+  echo "  Default behaviour with -d is to download the latest release version of Jalview for your architecture and install"
+  echo "  the .app in /Applications with user-space updates enabled."
+  echo "  These can be adjusted with the following options."
+  echo ""
+  echo "  -h           Show help"
+  echo "  -d           Download the latest DMG for the channel"
+  echo "  -m arch      (with -d) Download specific JVM architecture. Should be one of 'arm64' or 'x86_64' (defaults to \`uname -m\`)"
+  echo "  -c channel   (with -d) Download from channel. Should be one of 'release', 'test-release', 'develop' (defaults to 'release')."
+  echo "  -i filename  Use filename as DMG image file instead of downloading."
+  echo "  -a folder    Install the application .app bundle in folder (default '/Applications')."
+  echo "  -v           Verbose output."
+  echo "  -y           Assume 'yes' to confirmation."
+  echo "  -q           No output other than errors.  Assumes -y."
+  echo ""
+  echo "  Advanced options:"
+  echo ""
+  echo "  -u template  Use template for user-space updates path (defaults to '~/Library/Application Support/Jalview-Desktop')."
+  echo "               template should contain one of: a leading '~/', '%u' (user's username) or '%h' (user's home path)."
+  echo "  -U           Disable user-space updates (updates will be attempted in the installation folder)."
+  echo "  -S           Disable all user-space and installation updates."
+  echo "  -P           Do not add the jalview command-line path to /etc/paths.d."
+  echo "  -R           Do not perform the check for root privileges."
+  echo "  -t tmpdir    Use tmpdir for installation files (defaults to /tmp)"
+}
+
+# check OS
+UOS="$(uname -s)"
+if [ "$UOS" != "Darwin" ]; then
+  echo "This install script is for macOS."
+  echo "Jalview can be installed on other OSes using the installers found at"
+  echo "https://www.jalview.org/download/"
+  exit 1
+fi
+
+# set defaults
+DOWNLOAD=0
+UARCH=$(uname -m)
+CHANNEL="release"
+DMGFILE=""
+INSTALLERNAME="$(basename $0)"
+APPLICATIONFOLDER="/Applications"
+VERBOSE=0
+YES=0
+QUIET=0
+USERAPPDIRTEMPLATE=""
+DISABLEUSERAPPDIR=0
+DISABLEALLUPDATES=0
+DISABLEGLOBALPATH=0
+DISABLEROOTCHECK=0
+USETEMPDIR=""
+TMP="/tmp"
+
+CURLOPT=""
+RSYNCOPT=""
+DITTOOPT=""
+HDIUTILOPT="-quiet"
+
+# set options
+while getopts "hdi:a:vyqu:c:USPR" opt; do
+  case ${opt} in
+    h)
+      usage
+      exit 0
+      ;;
+    d)
+      DOWNLOAD=1
+      ;;
+    m)
+      UARCH="${OPTARG}"
+      ;;
+    c)
+      CHANNEL="${OPTARG}"
+      ;;
+    i)
+      DMGFILE="${OPTARG}"
+      DOWNLOAD=0
+      ;;
+    a)
+      APPLICATIONFOLDER="${OPTARG}"
+      ;;
+    v)
+      VERBOSE=1
+      CURLOPT="-v"
+      RSYNCOPT="-v"
+      DITTOOPT="-V"
+      HDIUTILOPT="-verbose"
+      ;;
+    y)
+      YES=1
+      ;;
+    q)
+      QUIET=1
+      CURLOPT="-s"
+      RSYNCOPT="-q"
+      HDIUTILOPT="-quiet"
+      YES=1
+      ;;
+    u)
+      USERAPPDIRTEMPLATE="${OPTARG}"
+      ;;
+    U)
+      DISABLEUSERAPPDIR=1
+      ;;
+    S)
+      DISABLEALLUPDATES=1
+      ;;
+    P)
+      DISABLEGLOBALPATH=1
+      ;;
+    R)
+      DISABLEROOTCHECK=1
+      ;;
+    t)
+      TMP="${OPTARG}"
+      ;;
+    *)
+      echo "Unrecognised option. Run with -h for help."
+      exit 2
+      ;;
+  esac
+done
+
+myecho() {
+  if [ "${QUIET}" != 1 ]; then
+    echo "* $1"
+  fi
+}
+
+# no -d or -i arguments
+if [ "${DOWNLOAD}" = 0 -a -z "${DMGFILE}" ]; then
+  echo "Please use one of -d or -i options"
+  echo ""
+  usage
+  exit 0
+fi
+
+# root permissions check
+if [ "${DISABLEROOTCHECK}" != 1 -a "${EUID}" != 0 ]; then
+  echo "This script should be run with root permissions, or disable this root check with -R."
+  exit 5
+fi
+
+# check channel
+if [ ! -z "$DMGFILE" -a -e "$DMGFILE" ]; then
+  myecho "Using DMG file '${DMGFILE}'"
+  CHANNEL="dmg"
+else
+  case "$CHANNEL" in
+    release|test-release|develop)
+      myecho "Using channel ${CHANNEL}"
+      ;;
+    *)
+      echo "-c channel must be one of 'release', 'test-release' or 'develop'"
+      exit 3
+      ;;
+  esac
+fi
+
+# convert uarch to jarch
+case $UARCH in
+  x86_64)
+    JARCH=x64
+    ;;
+  arm64)
+    JARCH=aarch64
+    ;;
+  *)
+    echo "Unknown architecture '$UARCH'. Exiting."
+    exit 4;
+  ;;
+esac
+
+# dir for downloads and volume mount
+TEMPDIR=$(mktemp -d -p "${TMP}" -t "${INSTALLERNAME%.sh}_${CHANNEL}")
+if [ "${DOWNLOAD}" = 1 ]; then
+  myecho "Using directory '${TEMPDIR}' to download and mount disk image"
+else
+  myecho "Using directory '${TEMPDIR}' to mount disk image"
+fi
+FILEBASE="${TEMPDIR}/jalview-${CHANNEL}-latest-macos-${JARCH}"
+VOLUMEDIR="${FILEBASE}.vol"
+
+# Confirmation of what's about to happen
+if [ "${YES}" != 1 ]; then
+  if [ "${DOWNLOAD}" = 1 ]; then
+    myecho "This script will download Jalview from the '${CHANNEL}' channel and install it into the '${APPLICATIONFOLDER}' folder."
+  else
+    myecho "This script will install Jalview from the '${DMGFILE}' disk image file into the '${APPLICATIONFOLDER}' folder."
+  fi
+
+  read -r -p "Continue? [y/N] " response
+  case $(echo "${response}" | tr '[:upper:]' '[:lower:]') in
+    yes|y)
+      myecho "Excellent! Continuing."
+      ;;
+    *)
+      echo "Aborting due to negative confirmation." && exit
+      ;;
+  esac
+fi
+
+if [ "${DOWNLOAD}" = 1 ]; then
+  DMGFILE="${FILEBASE}.dmg"
+  SHA256=""
+
+  URL="https://www.jalview.org/downloads/${CHANNEL}/installer/macos-${JARCH}"
+
+  myecho "Downloading '${URL}' to '${DMGFILE}'"
+  curl ${CURLOPT} -f -L -o "${DMGFILE}" "${URL}"
+  [ $? != 0 ] && echo "Could not download '$URL' to '$DMGFILE'" && exit 6
+  SHA256=$( curl ${CURLOPT} -f -s -L "${URL}.sha256" )
+  [ $? != 0 ] && echo "Could not download '$URL.sha256'" && exit 7
+
+  CHECK=$( shasum -a 256 "${DMGFILE}" | cut -d" " -f1 )
+  if [ "${CHECK}" = "${SHA256}" ]; then
+    myecho "Downloaded file '$DMGFILE' checksum matches downloaded checksum '$SHA256'"
+  else
+    echo "Downloaded file '$DMGFILE' checksum does not match downloaded checksum '$SHA256'"
+    exit 8
+  fi
+fi
+
+# mount the DMG image
+myecho "Mounting disk image '${DMGFILE}' on '${VOLUMEDIR}'"
+hdiutil attach ${HDIUTILOPT} -mountpoint "${VOLUMEDIR}" "${DMGFILE}"
+[ $? != 0 ] && echo "Could not mount '${DMGFILE}' on mount point '${VOLUMEDIR}'" && exit 9
+
+myecho "MOUNTED"
+
+# difficult to use a wildcard for a non-user-readable folder without inserting sudo directly into the command
+# i.e. what I really want to do here is
+# APP=$( sudo sh -c "ls -1d ${VOLUMEDIR}/Jalview*.app" | head -1 )
+# but don't want to put sudo in the script because of the -R option.  We'll loop through instead.
+VOLUMEFILES=$( ls -1 ${VOLUMEDIR} )
+while IFS= read -r VOLUMEFILE; do
+  FILE=$( basename "${VOLUMEFILE}" )
+  if [ "$FILE" != "${FILE#Jalview}" -a "$FILE" != "${FILE%.app}" ]; then
+    APP="$FILE"
+    break
+  fi
+done <<< "$VOLUMEFILES"
+[ -z "$APP" ] && echo "Could not find Jalview\*.app in the volume '${VOLUMEDIR}'" && exit 10
+
+APPNAME=$( basename "$APP" )
+myecho "Found application '${APP}'"
+
+# ensure no trailing slash on APPNAME or APPLICATIONFOLDER (important for rsync)
+while [ "${APPLICATIONFOLDER}" != "${APPLICATIONFOLDER%/}" -a "${APPLICATIONFOLDER}" != "/" ]; do
+  myecho "Removing trailing slash from APPLICATIONFOLDER='${APPLICATIONFOLDER}'"
+  APPLICATIONFOLDER="${APPLICATIONFOLDER%/}"
+done
+while [ "${APPNAME}" != "${APPNAME%/}" ]; do
+  myecho "Removing trailing slash from APPNAME='${APPNAME}'"
+  APPNAME=${APPNAME%/}
+done
+[ -z "$APPNAME" -o "${APPNAME%.app}" = "$APPNAME" ] && echo "Could not find suitable Jalview\*.app in the volume '${VOLUMEDIR}'" && exit 11
+
+## rsync .app from mounted volume to application folder
+#myecho "Rsyncing '${VOLUMEDIR}/${APPNAME}' to '${APPLICATIONFOLDER}/${APPNAME}'"
+#rsync ${RSYNCOPT} -ah --delete "${VOLUMEDIR}/${APPNAME}" "${APPLICATIONFOLDER}/"
+#[ $? != 0 ] && echo "Possible problem when rsyncing '${APP}' to '${APPLICATIONFOLDER}'" && exit 12
+#myecho "Finished rsync"
+
+# using ditto
+APPPATH="${APPLICATIONFOLDER}/${APPNAME}"
+OLDAPPPATH="${APPPATH}.old"
+myecho "Copying '${VOLUMEDIR}/${APPNAME}' to '${APPPATH}'"
+if [ -e "$APPPATH" ]; then
+  if [ -e "$OLDAPPPATH" ]; then
+    rm -Rf "$OLDAPPPATH"
+  fi
+  mv "$APPPATH" "$OLDAPPPATH"
+fi
+ditto ${DITTOOPT} "${VOLUMEDIR}/${APPNAME}" "$APPPATH" && [ -e "$OLDAPPPATH" ] && rm -Rf "$OLDAPPPATH"
+myecho "Finished copying '${APPNAME}'"
+
+EXIT=0
+declare -a WARNINGS=()
+addwarning() {
+  local W=$1
+  local N=$2
+  myecho "${W}"
+  WARNINGS=( "${WARNINGS[@]}" "${W}" )
+  # exit with the first warning value
+  [ "${EXIT}" = 0 ] && EXIT=$N
+}
+
+# unmount the DMG image
+myecho "Unmounting '${VOLUMEDIR}'"
+hdiutil detach ${HDIUTILOPT} "${VOLUMEDIR}" || addwarning "Possible problem when unmounting/deleting '${VOLUMEDIR}'.  I'm continuing the install but you should look at it later." 13
+
+# delete the image file and temp dir
+if [ "${DOWNLOAD}" = 1 ]; then
+  myecho "Deleting downloaded disk image file '${DMGFILE}'"
+  rm "${DMGFILE}"
+fi
+myecho "Removing temporary directory '${TEMPDIR}'"
+rmdir "${TEMPDIR}"
+
+VMOPTIONS="${APPLICATIONFOLDER}/${APPNAME}/Contents/vmoptions.txt"
+
+VMOPTIONS_START=""
+if [ -e "${VMOPTIONS}" ]; then
+  while IFS= read -r line; do
+    VMOPTIONS_START="${VMOPTIONS_START}${line}"$'\n'
+    if [ "${line}" != "${line/Jalview/}" ]; then # i.e. $line contains "Jalview"
+      break
+    fi
+  done < "${VMOPTIONS}"
+fi
+
+LONGHASH=$( printf %s "${APPLICATIONFOLDER}/${APPNAME}/Contents/Resources/app" | shasum -a 256 -b )
+HASH=${LONGHASH:0:8}
+NAME=${APPNAME%.app}
+
+myecho "Writing vmoptions files '${VMOPTIONS}'"
+VMOPTIONS_COMMENT=$( cat << EOM
+# Jalview options added by $( basename $0 ) at $( date +"%F %T")
+EOM
+)
+
+# -Dnouserdefaultappdir=false
+if [ "${DISABLEUSERAPPDIR}" = 1 ]; then
+  myecho "- User-space updates DISABLED"
+  VMOPTIONS_USERAPPDIR=$( cat << EOM
+#
+# Comment out the following line to allow user-space updates
+-Dnouserdefaultappdir=true
+EOM
+)
+else
+  myecho "- User-space updates enabled"
+  VMOPTIONS_USERAPPDIR=$( cat << EOM
+#
+# Uncomment the following line to disable user-space updates
+#-Dnouserdefaultappdir=true
+EOM
+)
+fi
+
+# -Dsetuserappdirpath
+if [ -z "${USERAPPDIRTEMPLATE}" ]; then
+  myecho "- Default user-space updates path (~/Library/Application Support/Jalview-Desktop/${NAME}/${HASH})"
+  VMOPTIONS_USERAPPDIRPATH=$( cat << EOM
+#
+# Uncomment the below line to set a custom path for user-space updates -- use with caution.
+# A leading ~/ or %h anywhere will be substituted with the user's home path, and %u by the username.
+# If not set, the default is ~/Library/Application Support/Jalview-Desktop for macOS
+#-Dsetuserappdirpath=/tmp/jalview/%u
+EOM
+)
+else
+  myecho "- CUSTOMISED user-space updates path (${USERAPPDIRTEMPLATE})"
+  VMOPTIONS_USERAPPDIRPATH=$( cat << EOM
+#
+# The below line sets a custom path for user-space updates -- use with caution.
+# A leading ~/ or %h anywhere will be substituted with the user's home path, and %u by the username.
+# If unset, the default is ~/Library/Application Support/Jalview-Desktop for macOS
+-Dsetuserappdirpath=${USERAPPDIRTEMPLATE}
+EOM
+)
+fi
+
+# -Dsilent=noupdate
+if [ "${DISABLEALLUPDATES}" = 1 ]; then
+  myecho "- All automatic updates DISABLED"
+  VMOPTIONS_UPDATES=$( cat << EOM
+#
+# Comment out the following line to enable updates
+-Dsilent=noupdate
+EOM
+)
+else
+  if [ "${DISABLEUSERAPPDIR}" = 1 ]; then
+    myecho "- Automatic updates enabled IN INSTALLATION"
+  else
+    myecho "- Automatic updates enabled in user-space"
+  fi
+  VMOPTIONS_UPDATES=$( cat << EOM
+#
+# Uncomment the following line to also disable all updates
+#-Dsilent=noupdate
+EOM
+)
+fi
+
+printf "%s\n%s\n%s\n%s\n%s\n" "${VMOPTIONS_START}" "${VMOPTIONS_COMMENT}" "${VMOPTIONS_USERAPPDIR}" "${VMOPTIONS_USERAPPDIRPATH}" "${VMOPTIONS_UPDATES}" > "${VMOPTIONS}" || addwarning "Possible problem adding options to '${VMOPTIONS}'" 14
+
+# set a global path in /etc/paths.d
+if [ "${DISABLEGLOBALPATH}" != 1 ]; then
+  US_NAME="${NAME// /_}"
+  PATHFILE="/etc/paths.d/${US_NAME}-${HASH}"
+  myecho "Writing global PATH to '${PATHFILE}'"
+  echo "${APPLICATIONFOLDER}/${APPNAME}/Contents/MacOS" > "${PATHFILE}" || addwarning "Possible problem writing path file '${PATHFILE}'" 15
+fi
+
+# show accumulated warnings
+if [ "${#WARNINGS[@]}" != 0 ]; then
+  myecho "-----------------"
+  myecho "Installation complete."
+  myecho "Warnings summary:"
+  myecho "$( printf -- '- %s\n' "${WARNINGS[@]}" )"
+else
+  myecho "Successful installation!"
+fi
+
+exit $EXIT
diff --git a/utils/install4j/warning.png b/utils/install4j/warning.png
new file mode 100644 (file)
index 0000000..2063df2
Binary files /dev/null and b/utils/install4j/warning.png differ
diff --git a/utils/osx_signing/sign_dmg.sh b/utils/osx_signing/sign_dmg.sh
new file mode 100755 (executable)
index 0000000..1b3973c
--- /dev/null
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+if [[ "$GITDIR" == "" ]]; then
+    GITDIR=~/uod-development/jalview-builds/git/jalview
+fi;
+
+if [[ "$DEVELOPERID" == "" ]]; then
+    DEVELOPERID="Developer ID"
+fi;
+
+if [[ "$TMPDMG" == "" ]]; then
+    TMPDMG="signingDMG"
+fi;
+
+echo APPNAME $APPNAME like Jalview Test
+echo doing ARCH $ARCH
+echo using entitlements from $GITDIR
+echo using key $DEVELOPERID
+
+FAPPNAME="${APPNAME/ /\\ }"
+FAPPNAMEESC="${APPNAME/ /\\\\\\ }"
+FWAPP="${APPNAME/ [A-Za-z]*/}"
+ARCHNAME="${APPNAME// /_}-${APPVER//\./_}-macos-$ARCH-java_$JVER"
+DMGNAME="${APPNAME/ /_}-${APPVER//\./_}-macos-$ARCH-java_$JVER.dmg"
+VOLNAME="${APPNAME// /_}\\ Installer\\ \\(${APPVER//\./_}\\ $ARCH\\ $JVER\\)"
+VLNAME="${APPNAME// /_} Installer (${APPVER//\./_} $ARCH $JVER)"
+BORINGVLNAME="${APPNAME} Installer"
+
+
+
+echo "will mount $DMGNAME as $VOLNAME"
+if [[ -d $TMPDMG ]]; then
+       echo "'$TMPDMG' is in the way. Please delete it or set TMPDMG"
+       exit 1;
+fi
+
+if [[ -f $DMGNAME ]]; then
+    hdiutil attach $DMGNAME
+    ditto /Volumes/${FWAPP}* $TMPDMG
+    hdiutil eject /Volumes/${FWAPP}*
+    mkdir -p unsigned
+    mv -v $DMGNAME unsigned/
+    echo Moved $DMGNAME to unsigned/$DMGNAME
+    codesign  --remove-signature --force --deep -vvvv -s "Developer ID" --options runtime --entitlements $GITDIR/utils/osx_signing/entitlements.txt $TMPDMG/${FWAPP}*.app/Contents/Resources/app/jre/Contents/MacOS/libjli.dylib 
+
+    codesign  --verify --deep -v ./$TMPDMG/${FWAPP}*.app/Contents/Resources/app/jre/Contents/MacOS/libjli.dylib 
+
+    codesign --remove-signature --force --deep -vvvv -s "Developer ID" --options runtime --entitlements $GITDIR/utils/osx_signing/entitlements.txt  $TMPDMG/${FWAPP}*.app/Contents/MacOS/JavaApplicationStub
+
+    hdiutil create -megabytes 260 -srcfolder ./$TMPDMG -volname "$BORINGVLNAME" $ARCHNAME.dmg
+
+    codesign --force --deep -vvvv -s "Developer ID" --options runtime --entitlements $GITDIR/utils/osx_signing/entitlements.txt $ARCHNAME.dmg
+
+    codesign --deep -vvvv $ARCHNAME.dmg
+    
+    rm -Rf $TMPDMG
+else
+    echo Can\'t find $DMGNAME - dit you set APPNAME APPVER ARCH and JVER correctly ?
+fi
+
diff --git a/utils/osx_signing/staple_dmg.sh b/utils/osx_signing/staple_dmg.sh
new file mode 100755 (executable)
index 0000000..0eb24dc
--- /dev/null
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+if [[ "$GITDIR" == "" ]]; then
+    GITDIR=~/uod-development/jalview-builds/git/jalview
+fi;
+
+
+if [[ "$DEVELOPERID" == "" ]]; then
+    DEVELOPERID="Developer ID"
+fi;
+
+if [[ "$TMPDMG" == "" ]]; then
+    TMPDMG="staplingDMG"
+fi;
+
+
+echo APPNAME $APPNAME like Jalview Test
+echo doing ARCH $ARCH
+echo using entitlements from $GITDIR
+
+FAPPNAME="${APPNAME/ /\\ }"
+FAPPNAMEESC="${APPNAME/ /\\\\\\ }"
+FWAPP="${APPNAME/ [A-Za-z]*/}"
+ARCHNAME="${APPNAME// /_}-${APPVER//\./_}-macos-$ARCH-java_$JVER"
+DMGNAME="${APPNAME/ /_}-${APPVER//\./_}-macos-$ARCH-java_$JVER.dmg"
+VOLNAME="${APPNAME// /_}\\ Installer\\ \\(${APPVER//\./_}\\ $ARCH\\ $JVER\\)"
+VLNAME="${APPNAME// /_} Installer (${APPVER//\./_} $ARCH $JVER)"
+BORINGVLNAME="${APPNAME} Installer"
+echo "will mount $DMGNAME as $VOLNAME"
+
+if [[ -d $TMPDMG ]]; then
+       echo "'$TMPDMG' is in the way. Please delete it or set TMPDMG"
+       exit 1;
+fi
+
+if [[ -f $DMGNAME ]]; then
+    hdiutil attach $DMGNAME
+    ditto /Volumes/${FWAPP}* $TMPDMG
+    hdiutil eject /Volumes/${FWAPP}*
+    xcrun stapler staple $TMPDMG/${FWAPP}*.app
+    mkdir -p stapled
+    hdiutil create -megabytes 240 -srcfolder $TMPDMG -volname "$BORINGVLNAME" stapled/$DMGNAME
+    codesign --force --deep -vvvv -s "$DEVELOPERID" --options runtime --entitlements ${GITDIR}/utils/osx_signing/entitlements.txt stapled/$DMGNAME
+    codesign --deep -vvvv stapled/$DMGNAME
+       echo "Stapled DMG is in stapled/$DMGNAME"
+    rm -Rf $TMPDMG    
+else
+    echo Can\'t find $DMGNAME - dit you set APPNAME APPVER ARCH and JVER correctly ?
+fi
+